Skip to content

Commit

Permalink
Adds integration API for HTTP POST uploads on the CC32xx web server. (#…
Browse files Browse the repository at this point in the history
…578)

- adds implementation to the NetAppRequestHandler
- adds callback storage holding POST callbacks by URI.
- adds API to register POST callback
- The first POST callback is called on the network thread, further uploaded
  data is acquired by additional calls on the CC32xxWiFi class.
- Adds POST response function on the CC32xxWiFi.

Homogenizes the calls for Get Token and Post Callbacks:
- Simplifies the API of registering Tokens
- Clarifies the documentation to avoid confusion of a Get Token with an
  HTTP GET operation.
- Reverses the order of pairs to match it with what an std::map<>::iterator has.
- Fixes a bug in string comparison for the token get implementation.
  • Loading branch information
balazsracz authored Oct 4, 2021
1 parent 802bfd5 commit c2ecedc
Show file tree
Hide file tree
Showing 2 changed files with 244 additions and 32 deletions.
177 changes: 161 additions & 16 deletions src/freertos_drivers/net_cc32xx/CC32xxWiFi.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ struct CC32xxWiFi::HttpServerEvent : public ::SlNetAppHttpServerEvent_t {};
/** CC32xx forward declaration Helper */
struct CC32xxWiFi::HttpServerResponse : public ::SlNetAppHttpServerResponse_t {};

/** CC32xx forward declaration Helper */
struct CC32xxWiFi::NetAppRequest : public ::SlNetAppRequest_t {};

/** CC32xx forward declaration Helper */
struct CC32xxWiFi::NetAppResponse : public ::SlNetAppResponse_t {};

/** CC32xx forward declaration Helper */
struct CC32xxWiFi::FatalErrorEvent : public ::SlDeviceFatal_t {};

Expand Down Expand Up @@ -192,8 +198,8 @@ CC32xxWiFi::CC32xxWiFi()
SL_SOCKET_FD_ZERO(&efds);
ssid[0] = '\0';

add_http_get_callback(make_pair(bind(&CC32xxWiFi::http_get_ip_address,
this), "__SL_G_UNA"));
add_http_get_token_callback(
"__SL_G_UNA", bind(&CC32xxWiFi::http_get_ip_address, this));
}

uint8_t CC32xxWiFi::security_type_to_simplelink(SecurityType sec_type)
Expand Down Expand Up @@ -1355,38 +1361,169 @@ void CC32xxWiFi::http_server_callback(HttpServerEvent *event,
case SL_NETAPP_EVENT_HTTP_TOKEN_GET:
{
unsigned char *ptr;

ptr = response->ResponseData.TokenValue.pData;
response->ResponseData.TokenValue.Len = 0;

for (unsigned i = 0; i < httpGetCallbacks_.size(); ++i)
string token((const char *)event->EventData.HttpTokenName.pData,
event->EventData.HttpTokenName.Len);
LOG(VERBOSE, "token get %s", token.c_str());
{
if (strcmp((const char *)event->EventData.HttpTokenName.pData,
httpGetCallbacks_[i].second) == 0)
OSMutexLock l(&lock_);
for (unsigned i = 0; i < httpGetTokenCallbacks_.size(); ++i)
{
string result = httpGetCallbacks_[i].first();
// clip string if required
if (result.size() >= SL_NETAPP_MAX_TOKEN_VALUE_LEN)
if (token == httpGetTokenCallbacks_[i].first)
{
result.erase(SL_NETAPP_MAX_TOKEN_VALUE_LEN);
string result = httpGetTokenCallbacks_[i].second();
// clip string if required
if (result.size() >= SL_NETAPP_MAX_TOKEN_VALUE_LEN)
{
result.resize(SL_NETAPP_MAX_TOKEN_VALUE_LEN);
}
memcpy(ptr, result.data(), result.size());
ptr += result.size();
response->ResponseData.TokenValue.Len += result.size();
break;
}
memcpy(ptr, result.c_str(), result.size());
ptr += result.size();
response->ResponseData.TokenValue.Len += result.size();
break;
}
}
break;
}
case SL_NETAPP_EVENT_HTTP_TOKEN_POST:
{
string token(
(const char *)event->EventData.HttpPostData.TokenName.pData,
event->EventData.HttpPostData.TokenName.Len);
string val(
(const char *)event->EventData.HttpPostData.TokenValue.pData,
event->EventData.HttpPostData.TokenValue.Len);
LOG(VERBOSE, "token post %s=%s", token.c_str(), val.c_str());
break;
}
default:
break;
}
}

/*
* CC32xxWiFi::netapp_request_callback()
*/
void CC32xxWiFi::netapp_request_callback(
NetAppRequest *request, NetAppResponse *response)
{
if (!request || !response || request->AppId != SL_NETAPP_HTTP_SERVER_ID)
{
return;
}

string uri;
uint32_t content_length = 0xFFFFFFFFu;

uint8_t *meta_curr = request->requestData.pMetadata;
uint8_t *meta_end = meta_curr + request->requestData.MetadataLen;
while (meta_curr < meta_end)
{
uint8_t meta_type = *meta_curr; /* meta_type is one byte */
meta_curr++;
uint16_t meta_len = *(_u16 *)meta_curr; /* Length is two bytes */
meta_curr += 2;
switch (meta_type)
{
case SL_NETAPP_REQUEST_METADATA_TYPE_HTTP_CONTENT_LEN:
memcpy(&content_length, meta_curr, meta_len);
break;
case SL_NETAPP_REQUEST_METADATA_TYPE_HTTP_REQUEST_URI:
uri.assign((const char *)meta_curr, meta_len);
break;
}
meta_curr += meta_len;
}
// Do we have more data to come?
bool has_more = request->requestData.Flags &
SL_NETAPP_REQUEST_RESPONSE_FLAGS_CONTINUATION;
bool found = false;
switch (request->Type)
{
case SL_NETAPP_REQUEST_HTTP_POST:
{
OSMutexLock l(&lock_);
for (unsigned i = 0; i < httpPostCallbacks_.size(); ++i)
{
if (uri == httpPostCallbacks_[i].first)
{
found = true;
httpPostCallbacks_[i].second(request->Handle,
content_length, request->requestData.pMetadata,
request->requestData.MetadataLen,
request->requestData.pPayload,
request->requestData.PayloadLen, has_more);
break;
}
}
if (found)
{
break;
}
}
// Fall through to 404.
default:
response->Status = SL_NETAPP_HTTP_RESPONSE_404_NOT_FOUND;
response->ResponseData.pMetadata = NULL;
response->ResponseData.MetadataLen = 0;
response->ResponseData.pPayload = NULL;
response->ResponseData.PayloadLen = 0;
response->ResponseData.Flags = 0;
return;
}
}

/*
* CC32xxWiFi::get_post_data()
*/
bool CC32xxWiFi::get_post_data(uint16_t handle, void *buf, size_t *len)
{
uint16_t ulen = *len;
uint32_t flags;
int ret = sl_NetAppRecv(handle, &ulen, (uint8_t *)buf, &flags);
if (ret < 0 || (flags & SL_NETAPP_REQUEST_RESPONSE_FLAGS_ERROR))
{
*len = 0;
return false;
}
*len = ulen;
return (flags & SL_NETAPP_REQUEST_RESPONSE_FLAGS_CONTINUATION) != 0;
}

/*
* CC32xxWiFi::send_post_response()
*/
void CC32xxWiFi::send_post_respose(
uint16_t handle, uint16_t http_status, const string &redirect)
{
// Pieces together a valid metadata structure for SimpleLink.
string md;
uint16_t len;
if (!redirect.empty())
{
http_status = SL_NETAPP_HTTP_RESPONSE_302_MOVED_TEMPORARILY;
}
md.push_back(SL_NETAPP_REQUEST_METADATA_TYPE_STATUS);
len = 2;
md.append((const char *)&len, 2);
md.append((const char *)&http_status, 2);
if (!redirect.empty())
{
md.push_back(SL_NETAPP_REQUEST_METADATA_TYPE_HTTP_LOCATION);
len = redirect.size();
md.append((const char *)&len, 2);
md += redirect;
}
// Now we need to move the metadata content to a buffer that is
// malloc'ed. This buffer will be freed asynchronously through a callback.
void *buf = malloc(md.size());
memcpy(buf, md.data(), md.size());

uint32_t flags = SL_NETAPP_REQUEST_RESPONSE_FLAGS_METADATA;
sl_NetAppSend(handle, md.size(), (uint8_t *)buf, flags);
}

/*
* CC32xxWiFi::fatal_error_callback()
Expand Down Expand Up @@ -1521,7 +1658,15 @@ void SimpleLinkHttpServerEventHandler(
void SimpleLinkNetAppRequestEventHandler(SlNetAppRequest_t *pNetAppRequest,
SlNetAppResponse_t *pNetAppResponse)
{
/* Unused in this application */
LOG(VERBOSE,
"netapprq app %d type %d hdl %d mdlen %u payloadlen %u flags %u",
pNetAppRequest->AppId, pNetAppRequest->Type, pNetAppRequest->Handle,
pNetAppRequest->requestData.MetadataLen,
pNetAppRequest->requestData.PayloadLen,
(unsigned)pNetAppRequest->requestData.Flags);
CC32xxWiFi::instance()->netapp_request_callback(
static_cast<CC32xxWiFi::NetAppRequest *>(pNetAppRequest),
static_cast<CC32xxWiFi::NetAppResponse *>(pNetAppResponse));
}

/**
Expand Down
99 changes: 83 additions & 16 deletions src/freertos_drivers/net_cc32xx/CC32xxWiFi.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@ public:
/** CC32xx SimpleLink forward declaration */
struct HttpServerResponse;

/** CC32xx SimpleLink forward declaration */
struct NetAppRequest;

/** CC32xx SimpleLink forward declaration */
struct NetAppResponse;

/** CC32xx SimpleLink forward declaration */
struct FatalErrorEvent;

Expand Down Expand Up @@ -151,6 +157,24 @@ public:
int rssi; /**< receive signal strength indicator of the AP */
};

/** This function type is used for POST callback operations to the
* application.
* @param handle the operation handle, needs to be provided to the future
* operations to fetch followup data and send response.
* @param content_length value of the Content-Length header, or -1 if such
* a header is not found.
* @param md encoded metadata. See the CC32xx documentation on how metadata
* is encoded. The lifetime is restricted to this call inline.
* @param md_len number of bytes in the metadata array
* @param payload the content (or beginning of the content). The lifetime is
* restricted to this call inline.
* @param payload_len how many bytes are in this chunk of the content
* @param has_more true if there is a continuation of the payload, which
* needs to be fetched with get_post_data. */
using PostFunction = std::function<void(uint16_t handle,
uint32_t content_length, const uint8_t *md, size_t md_len,
const uint8_t *payload, size_t payload_len, bool has_more)>;

/** Constructor.
*/
CC32xxWiFi();
Expand Down Expand Up @@ -425,12 +449,14 @@ public:
* isthe function to execute.*/
void run_on_network_thread(std::function<void()> callback);

/** Add an HTTP get token callback. A get token is a string that takes the
* form "__SL_G_*". The form "__SL_G_U*" is the form that is reserved for
* user defined tokens. The * can be any two characters that uniquely
* identify the token. When the token is found in an HTML file, the
* network processor will call the supplied callback in order for the user
* to return the resulting string. The result returned will be clipped at
/** Add an HTTP get token callback. A get token is a simple macro
* substitution that is applied to all files (e.g. HTML, JS) served by the
* builtin webserver of the CC32xx. The token has a fixed form "__SL_G_*".
* The form "__SL_G_U*" is the form that is reserved for user defined
* tokens. The * can be any two characters that uniquely identify the
* token. When the token is found in an HTML file, the network processor
* will call the supplied callback in order for the user to return the
* substitution string. The result returned will be clipped at
* (MAX_TOKEN_VALUE_LEN - 1), which is (64 - 1) bytes. All tokens must be
* an exact match.
*
Expand All @@ -441,7 +467,7 @@ public:
* public:
* SomeClass()
* {
* add_http_get_callback(std::make_pair(std::bind(&SomeClass::http_get, this), "__SL_G_U.A");
* add_http_get_token_callback("__SL_G_U.A", std::bind(&SomeClass::http_get, this));
* }
*
* private:
Expand All @@ -460,18 +486,48 @@ public:
* <a href="http://www.ti.com/lit/ug/swru368a/swru368a.pdf">
* CC3100/CC3200 SimpleLink Wi-Fi Internet-on-a-Chip User's Guide</a>
*
* @param callback the std::pair<> of the function to execute and the
* matching token to execute the callback on. The second (const
* char *) argument of the std::pair must live for as long as the
* callback is valid.
* @param token_name The token name to match. Must live for the entire
* lifetime of the binary. Must be of the form __SL_G_U??
* @param callback the function to execute to give the replacement.
*/
void add_http_get_callback(std::pair<std::function<std::string()>,
const char *> callback)
void add_http_get_token_callback(
const char *token_name, std::function<std::string()> callback)
{
OSMutexLock l(&lock_);
httpGetCallbacks_.emplace_back(std::move(callback));
httpGetTokenCallbacks_.emplace_back(token_name, std::move(callback));
}

/** Registers a handler for an HTTP POST operation.
* @param uri the target of the form submit, of the format "/foo/bar"
* @param callback this function will be called from the network processor
* context when a POST happens to the given URI.
*/
void add_http_post_callback(const char *uri, PostFunction callback)
{
OSMutexLock l(&lock_);
httpPostCallbacks_.emplace_back(uri, std::move(callback));
}

/** Retrieves additional payload for http POST operations. This function
* blocks the calling thread. After the lat chunk is retrieved, the caller
* must invoke the post response function.
* @param handle the POST operation handle, given by the POST callback.
* @param buf where to deposit additional data.
* @param len at input, set to the max number of bytes to store. Will be
* overwritten by the number of actual bytes that arrived.
* @return true if there is additional data that needs to be fetched, false
* if this was the last chunk. */
bool get_post_data(uint16_t handle, void *buf, size_t *len);

/** Sends a POST response.
* @param handle the POST operation handle, given by the POST callback.
* @param code HTTP error code (e.g. 204 for success).
* @param redirect optional, if present, will send back a 302 redirect
* status with this URL (http_status will be ignored).
*/
void send_post_respose(uint16_t handle, uint16_t http_status = 204,
const string &redirect = "");

/** This function handles WLAN events. This is public only so that an
* extern "C" method can call it. DO NOT use directly.
* @param event pointer to WLAN Event Info
Expand Down Expand Up @@ -499,6 +555,14 @@ public:
void http_server_callback(HttpServerEvent *event,
HttpServerResponse *response);

/** This function handles netapp request callbacks. This is public
* only so that an extern "C" method can call it. DO NOT use directly.
* @param request pointer to NetApp Request info
* @param response pointer to NetApp Response info
*/
void netapp_request_callback(
NetAppRequest *request, NetAppResponse *response);

/** This Function Handles the Fatal errors
* @param event - Contains the fatal error data
* @return None
Expand Down Expand Up @@ -586,8 +650,11 @@ private:
std::vector<std::function<void()> > callbacks_;

/// List of callbacks for http get tokens
std::vector<std::pair<std::function<std::string()>, const char *>>
httpGetCallbacks_;
std::vector<std::pair<const char *, std::function<std::string()>>>
httpGetTokenCallbacks_;

/// List of callbacks for http post handlers
std::vector<std::pair<const char *, PostFunction>> httpPostCallbacks_;

/// Protects callbacks_ vector.
OSMutex lock_;
Expand Down

0 comments on commit c2ecedc

Please sign in to comment.