Skip to content

Commit

Permalink
refactor query decoding
Browse files Browse the repository at this point in the history
Replace redundant code in several handlers with use of these macros
defined and documented in webserver.h:

STANDARD_DECODE_QUERY
STANDARD_DECODE_PARAMETER
STANDARD_DECODE_SOLE_PARAMETER

This helps ensure consistency in processing and error handling.
  • Loading branch information
jeffkowalski committed Jun 29, 2024
1 parent a37308d commit 141927b
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 128 deletions.
108 changes: 83 additions & 25 deletions include/webserver.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,89 @@
extern void start_webserver();
extern bool url_decode_in_place (char * str);

extern esp_err_t handler_frequency_get(httpd_req_t *);
extern esp_err_t handler_frequency_put(httpd_req_t *);
extern esp_err_t handler_mode_get(httpd_req_t *);
extern esp_err_t handler_mode_put(httpd_req_t *);
extern esp_err_t handler_rxBandwidth_get(httpd_req_t *);
extern esp_err_t handler_rxBandwidth_put(httpd_req_t *);
extern esp_err_t handler_prepareft8_post(httpd_req_t *);
extern esp_err_t handler_ft8_post(httpd_req_t *);
extern esp_err_t handler_cancelft8_post(httpd_req_t *);
extern esp_err_t handler_batteryPercent_get(httpd_req_t *);
extern esp_err_t handler_batteryVoltage_get(httpd_req_t *);
extern esp_err_t handler_connectionStatus_get(httpd_req_t *);
extern esp_err_t handler_time_put(httpd_req_t *);
extern esp_err_t handler_settings_get(httpd_req_t *);
extern esp_err_t handler_settings_post(httpd_req_t *);
extern esp_err_t handler_version_get(httpd_req_t *);

#define REPLY_WITH_FAILURE(req, code, message) do {\
ESP_LOGE(TAG8, message);\
httpd_resp_send_##code(req);\
return ESP_FAIL;\
extern esp_err_t handler_frequency_get (httpd_req_t *);
extern esp_err_t handler_frequency_put (httpd_req_t *);
extern esp_err_t handler_keyer_put (httpd_req_t *);
extern esp_err_t handler_mode_get (httpd_req_t *);
extern esp_err_t handler_mode_put (httpd_req_t *);
extern esp_err_t handler_msg_put (httpd_req_t *);
extern esp_err_t handler_power_get (httpd_req_t *);
extern esp_err_t handler_power_put (httpd_req_t *);
extern esp_err_t handler_rxBandwidth_get (httpd_req_t *);
extern esp_err_t handler_rxBandwidth_put (httpd_req_t *);
extern esp_err_t handler_prepareft8_post (httpd_req_t *);
extern esp_err_t handler_ft8_post (httpd_req_t *);
extern esp_err_t handler_cancelft8_post (httpd_req_t *);
extern esp_err_t handler_batteryPercent_get (httpd_req_t *);
extern esp_err_t handler_batteryVoltage_get (httpd_req_t *);
extern esp_err_t handler_connectionStatus_get (httpd_req_t *);
extern esp_err_t handler_time_put (httpd_req_t *);
extern esp_err_t handler_settings_get (httpd_req_t *);
extern esp_err_t handler_settings_post (httpd_req_t *);
extern esp_err_t handler_version_get (httpd_req_t *);

/**
* Helper definition, to be used within a function body.
* Retrieves a URL query from an httpd request.
*
* @param req the httpd_req_t object containing the request
* @param unsafe_buf a pointer to a newly allocated object containing the query
*/
#define STANDARD_DECODE_QUERY(req, unsafe_buf) \
/* Get the length of the URL query */ \
size_t buf_len = httpd_req_get_url_query_len (req) + 1; \
if (buf_len <= 1) \
REPLY_WITH_FAILURE (req, 404, "missing query string"); \
std::unique_ptr<char[]> buf (new char[buf_len]); \
if (!buf) \
REPLY_WITH_FAILURE (req, 500, "heap allocation failed"); \
char * unsafe_buf = buf.get(); /* reference to an ephemeral buffer */ \
/* Get the URL query */ \
if (httpd_req_get_url_query_str (req, unsafe_buf, buf_len) != ESP_OK) \
REPLY_WITH_FAILURE (req, 404, "query parsing error"); \
ESP_LOGV (TAG8, "request buffer[%d] = \"%s\"", buf_len, unsafe_buf);


/**
* Helper definition, to be used within a function body.
* Given a query string, extracts on parameter by name.
*
* @param unsafe_buf buffer containing the complete query
* @param param_name name of the query parameter
* @param param_value extracted value of the named query parameter
*/
#define STANDARD_DECODE_PARAMETER(unsafe_buf, param_name, param_value) \
char param_value[64] = {0}; \
if (httpd_query_key_value (unsafe_buf, param_name, param_value, sizeof (param_value)) != ESP_OK) \
REPLY_WITH_FAILURE (req, 404, "parameter parsing error");

/**
* Helper definition, to be used within a function body.
* Retrieves a query parameter by name, given an httpd_req_t.
*
* Note that this helper can be used only once per scoped block. This just a
* streamlined definition for the common case of a sole parameter. If you need
* to retrieve multiple parameters, then use STANDARD_DECODE_QUERY, and then as
* many STANDARD_DECODE_PARAMETER invocations as required.
*
* @param req the httpd_req_t object containing the request
* @param param_name name of the query parameter
* @param param_value extracted value of the named query parameter
*/
#define STANDARD_DECODE_SOLE_PARAMETER(req, param_name, param_value) \
STANDARD_DECODE_QUERY (req, unsafe_buf); \
STANDARD_DECODE_PARAMETER (unsafe_buf, param_name, param_value);

#define REPLY_WITH_FAILURE(req, code, message) \
do { \
ESP_LOGE (TAG8, message); \
httpd_resp_send_##code (req); \
return ESP_FAIL; \
} while (0)

#define REPLY_WITH_SUCCESS() do {\
ESP_LOGD(TAG8, "success");\
httpd_resp_send(req, "OK", HTTPD_RESP_USE_STRLEN);\
return ESP_OK;\
#define REPLY_WITH_SUCCESS() \
do { \
ESP_LOGD (TAG8, "success"); \
httpd_resp_send (req, "OK", HTTPD_RESP_USE_STRLEN); \
return ESP_OK; \
} while (0)
19 changes: 1 addition & 18 deletions src/handler_frequency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,24 +49,7 @@ esp_err_t handler_frequency_put(httpd_req_t *req)

ESP_LOGV(TAG8, "trace: %s()", __func__);

// Get the length of the URL query
size_t buf_len = httpd_req_get_url_query_len(req) + 1;
if (buf_len <= 1)
REPLY_WITH_FAILURE(req, 404, "missing query string");

std::unique_ptr<char[]> buf(new char[buf_len]);
if (!buf)
REPLY_WITH_FAILURE(req, 500, "heap allocation failed");
char * unsafe_buf = buf.get(); // reference to an ephemeral buffer

// Get the URL query
if (httpd_req_get_url_query_str(req, unsafe_buf, buf_len) != ESP_OK)
REPLY_WITH_FAILURE(req, 404, "query parsing error");

char param_value[32];
if (httpd_query_key_value(unsafe_buf, "frequency", param_value, sizeof(param_value)) != ESP_OK)
REPLY_WITH_FAILURE(req, 404, "parameter parsing error");

STANDARD_DECODE_SOLE_PARAMETER(req, "frequency", param_value)
int freq = atoi(param_value); // Convert the parameter to an integer
ESP_LOGI(TAG8, "freqency %d", freq);
if (freq <= 0)
Expand Down
60 changes: 11 additions & 49 deletions src/handler_ft8.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,32 +254,18 @@ esp_err_t handler_prepareft8_post(httpd_req_t *req)
if (CommandInProgress || ft8ConfigInfo != NULL)
REPLY_WITH_FAILURE(req, 500, "prepare called while another command already in progress");

/**
* In this short window of time between the check of CommandInProgress above,
* and the setting of the same to "true" below, we could have a race condition.
* But we'll assume it's rare, while we quickly decode the request.
* Otherwise, setting CommandInProgress to "true" beforehand would mean we would
* need to unwind the decoding macro to insert code setting it to "false" on error.
*/
STANDARD_DECODE_QUERY(req, unsafe_buf);

CommandInProgress = true;
gpio_set_level(LED_BLUE, LED_ON); // LED on

// Get the length of the URL query
size_t buf_len = httpd_req_get_url_query_len(req) + 1;
if (buf_len <= 1) {
CommandInProgress = false;
REPLY_WITH_FAILURE(req, 404, "missing query string");
}

std::unique_ptr<char[]> buf(new char[buf_len]);
if (!buf)
{
CommandInProgress = false;
REPLY_WITH_FAILURE(req, 500, "heap allocation failed");
}
char * unsafe_buf = buf.get(); // reference to an ephemeral buffer

// Get the URL query
if (httpd_req_get_url_query_str(req, unsafe_buf, buf_len) != ESP_OK)
{
CommandInProgress = false;
REPLY_WITH_FAILURE(req, 404, "query parsing error");
}
ESP_LOGV(TAG8, "request buffer[%d] = \"%s\"", buf_len, unsafe_buf);

char ft8_msg[64];
char nowTimeUTCms_str[64];
int64_t nowTimeUTCms = 0;
Expand Down Expand Up @@ -409,6 +395,8 @@ esp_err_t handler_ft8_post(httpd_req_t *req)
if (ft8TaskInProgress)
REPLY_WITH_FAILURE(req, 500, "post called while another FT8 task already in progress");

STANDARD_DECODE_QUERY(req, unsafe_buf);

CommandInProgress = true;

if (CancelRadioFT8ModeTime <= 0 && ft8ConfigInfo == NULL)
Expand All @@ -423,37 +411,11 @@ esp_err_t handler_ft8_post(httpd_req_t *req)
}
}

// We need to reparse the frequency and audio frequency from the query string
// Get the length of the URL query
size_t buf_len = httpd_req_get_url_query_len(req) + 1;
if (buf_len <= 1)
{
CommandInProgress = false;
REPLY_WITH_FAILURE(req, 404, "missing query string");
}

std::unique_ptr<char[]> buf(new char[buf_len]);
if (!buf)
{
CommandInProgress = false;
REPLY_WITH_FAILURE(req, 500, "heap allocation failed");
}
char * unsafe_buf = buf.get(); // reference to an ephemeral buffer

// Get the URL query
if (httpd_req_get_url_query_str(req, unsafe_buf, buf_len) != ESP_OK)
{
CommandInProgress = false;
REPLY_WITH_FAILURE(req, 404, "query parsing error");
}

char rfFreq_str[32];
long rfFreq = 0;
char audioFreq_str[16];
int audioFreq = 0;

ESP_LOGV(TAG8, "request buffer[%d] = \"%s\"", buf_len, unsafe_buf);

// Parse the 'messageText' parameter from the query
if (!(httpd_query_key_value(unsafe_buf, "rfFrequency", rfFreq_str, sizeof(rfFreq_str)) == ESP_OK &&
(rfFreq = atol(rfFreq_str)) > 0 &&
Expand Down
20 changes: 1 addition & 19 deletions src/handler_mode_bandwidth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,25 +105,7 @@ esp_err_t handler_rxBandwidth_put(httpd_req_t *req)

ESP_LOGV(TAG8, "trace: %s()", __func__);

// Get the length of the URL query
size_t buf_len = httpd_req_get_url_query_len(req) + 1;
if (buf_len <= 1)
REPLY_WITH_FAILURE(req, 404, "missing query string");

std::unique_ptr<char[]> buf(new char[buf_len]);
if (!buf)
REPLY_WITH_FAILURE(req, 500, "heap allocation failed");
char * unsafe_buf = buf.get(); // reference to an ephemeral buffer

// Get the URL query
if (httpd_req_get_url_query_str(req, unsafe_buf, buf_len) != ESP_OK)
REPLY_WITH_FAILURE(req, 404, "query parsing error");

char bw[32] = {0};
// Parse the 'bw' parameter from the query
if (httpd_query_key_value(buf.get(), "bw", bw, sizeof(bw)) != ESP_OK)
REPLY_WITH_FAILURE(req, 404, "parameter parsing error");

STANDARD_DECODE_SOLE_PARAMETER(req, "bw", bw);
ESP_LOGI(TAG8, "requesting bw = '%s'", bw);

{
Expand Down
18 changes: 1 addition & 17 deletions src/handler_time.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,23 +141,7 @@ esp_err_t handler_time_put(httpd_req_t *req)

ESP_LOGV(TAG8, "trace: %s()", __func__);

// Get the length of the URL query
size_t buf_len = httpd_req_get_url_query_len(req) + 1;
if (buf_len <= 1)
REPLY_WITH_FAILURE(req, 404, "missing query string");

std::unique_ptr<char[]> buf(new char[buf_len]);
if (!buf)
REPLY_WITH_FAILURE(req, 500, "heap allocation failed");
char * unsafe_buf = buf.get(); // reference to an ephemeral buffer

// Get the URL query
if (httpd_req_get_url_query_str(req, unsafe_buf, buf_len) != ESP_OK)
REPLY_WITH_FAILURE(req, 404, "query parsing error");

char param_value[32];
if (httpd_query_key_value(unsafe_buf, "time", param_value, sizeof(param_value)) != ESP_OK)
REPLY_WITH_FAILURE(req, 404, "parameter parsing error");
STANDARD_DECODE_SOLE_PARAMETER (req, "time", param_value);

if (!set_time(param_value))
REPLY_WITH_FAILURE(req, 500, "failed to set time");
Expand Down

0 comments on commit 141927b

Please sign in to comment.