LWIP官方httpd使用之GET
前言
httpd的移植可以參考上篇文章LWIP官方DEMO使用之httpd服務(wù) - USTHzhanglu - 博客園 (cnblogs.com)
此博文為學(xué)習(xí)筆記,僅介紹如何使用官方demo,無(wú)更深入分析。
此博文介紹了如何通過(guò)GET返回各種數(shù)據(jù)。
關(guān)鍵詞:LWIP, HTTP, HTTPD, GET
| LWIP版本 | lwip-STABLE-2_2_0_RC1 |
GET
GET
最常見(jiàn)的一種請(qǐng)求方式,當(dāng)客戶端要從服務(wù)器中讀取文檔時(shí),當(dāng)點(diǎn)擊網(wǎng)頁(yè)上的鏈接或者通過(guò)在瀏覽器的地址欄輸入網(wǎng)址來(lái)瀏覽網(wǎng)頁(yè)的,使用的都是GET方式。GET方法要求服務(wù)器將URL定位的資源放在響應(yīng)報(bào)文的數(shù)據(jù)部分,回送給客戶端。使用GET方法時(shí),請(qǐng)求參數(shù)和對(duì)應(yīng)的值附加在URL后面,利用一個(gè)問(wèn)號(hào)(“?”)代表URL的結(jié)尾與請(qǐng)求參數(shù)的開(kāi)始,傳遞參數(shù)長(zhǎng)度受限制。例如,/index.jsp?id=100&op=bind,這樣通過(guò)GET方式傳遞的數(shù)據(jù)直接表示在地址中。
訪問(wèn)網(wǎng)頁(yè)本身就是一個(gè)GET請(qǐng)求(畢竟本質(zhì)上是從server獲取html網(wǎng)頁(yè)數(shù)據(jù)后展示),在訪問(wèn)lwip.local時(shí),debug時(shí)信息如下:
ttp_accept 1ffead78 / 00000000
http_recv: pcb=1ffead78 pbuf=1ffedd84 err=Ok.
Received 487 bytes
First pbuf
CRLF received, parsing request
Received GET request
Received GET request for URI: /
Looking for /index.shtml...
Looking for /index.ssi...
Looking for /index.shtm...
Looking for /index.html...
Opened.
嘗試用GET獲取圖片數(shù)據(jù):
可以看到,返回了一個(gè)圖片數(shù)據(jù)。
通過(guò)makefsdata,將需要的資源提前燒錄到flash中,可以很方便的通過(guò)GET url的方式獲取。
問(wèn)題來(lái)了,假如想動(dòng)態(tài)讀取MCU里的某種數(shù)據(jù)呢?
類似于訪問(wèn)https://dog.ceo/api/breeds/image/random
會(huì)得到一個(gè)json數(shù)據(jù),其中包含了一個(gè)隨機(jī)的dog圖片url
{
message: https://images.dog.ceo/breeds/coonhound/n02089078_3191.jpg,
status: success
}
這種是沒(méi)法通過(guò)提前燒錄資源實(shí)現(xiàn)的。
一般情況下,我們會(huì)想到解析到對(duì)應(yīng)url后,手動(dòng)拼接字符串并返回來(lái)實(shí)現(xiàn):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
int main() {
char* url = http://example.com;
char* method = GET;
char* headers[] = {
Content-Type: application/json,
Accept: application/json
};
int header_count = sizeof(headers) / sizeof(char*);
char* body = { \current_time\: \;
time_t t = time(NULL);
char* time_str = ctime(&t);
body += time_str;
body += }\;
int len = strlen(url) + strlen(method) + header_count * sizeof(char*) + strlen(body);
char* full_url = malloc(len + 1);
strcpy(full_url, url);
strcat(full_url, );
strcat(full_url, method);
for (int i = 0; i < header_count; i++) {
strcat(full_url, \r);
strcat(full_url, headers[i]);
}
strcat(full_url, \r\r);
strcat(full_url, body);
printf(Full URL: %s, full_url);
free(full_url);
return 0;
}
運(yùn)行后輸出的結(jié)果如下:
Full URL: http://example.com GET
Content-Type: application/json
Accept: application/json
{ current_time: Thu Jan 26 16:14:03 2023 }
以上方法太繁瑣了,而且既然用LWIP了,在自己拼接字符串,實(shí)在是太過(guò)愚蠢了。
下面將介紹如何通過(guò)自帶的API實(shí)現(xiàn)GET動(dòng)態(tài)數(shù)據(jù)功能
genfiles_example
在官方示例中,有一個(gè)文件生成的示例genfiles_example:
lwip-STABLE-2_2_0_RC1/contrib/examples/httpd
$ tree -L 1
.
|-- cgi_example
|-- examples_fs
|-- examples_fsdata.c
|-- fs_example
|-- genfiles_example
|-- https_example
|-- post_example
`-- ssi_example
查看介紹如下:
/** * @file * HTTPD custom file system example for runtime generated files * * This file demonstrates how to add support for generated files to httpd. */
可以看到,該示例展示了如何在運(yùn)行時(shí)實(shí)時(shí)生成文件,也即是實(shí)時(shí)生成返回?cái)?shù)據(jù)。
導(dǎo)入
要使用該示例,首先要開(kāi)啟如下幾個(gè)選項(xiàng):
#define LWIP_HTTPD_CUSTOM_FILES 1
#define LWIP_HTTPD_FILE_EXTENSION 1
#define LWIP_HTTPD_DYNAMIC_HEADERS 1
#define LWIP_HTTPD_EXAMPLE_GENERATEDFILES 1
然后在工程中加入genfiles_example.c即可。
編譯燒錄,然后訪問(wèn) <lwip.local/generated.html>
自定義
官方示例僅展示了如何生成一個(gè)html文件,如果需要生成其他文件,我們還需要修改一部分代碼。
官方貼心的給了提示:
/* * Generating custom things instead of memcpy is left to your imagination :-) */ /* instead of doing memcpy, you would generate e.g. a JSON here */ memcpy(file->pextension, generated_html, sizeof(generated_html));
還是先用memcpy,把html數(shù)據(jù)替換成json看看:
static const char JSON_STRING[] =
"{\"user\": \"johndoe\", \"admin\": false, \"uid\": 1000,\n "
"\"groups\": [\"users\", \"wheel\", \"audio\", \"video\"]}";
int
fs_open_custom(struct fs_file *file, const char *name)
{
/* this example only provides one file */
if (!strcmp(name, "/generated.html")) {
/* initialize fs_file correctly */
memset(file, 0, sizeof(struct fs_file));
file->pextension = mem_malloc(sizeof(JSON_STRING));
if (file->pextension != NULL) {
/* instead of doing memcpy, you would generate e.g. a JSON here */
memcpy(file->pextension, JSON_STRING, sizeof(JSON_STRING));
file->data = (const char *)file->pextension;
file->len = sizeof(JSON_STRING) - 1; /* don't send the trailing 0 */
file->index = file->len;
/* allow persisteng connections */
file->flags = FS_FILE_FLAGS_HEADER_PERSISTENT;
return 1;
}
}
return 0;
}
返回值
GET /generated.html HTTP/1.1
Host: lwip.local
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
HTTP/1.0 200 OK
Server: lwIP/2.2.0rc1 (http://savannah.nongnu.org/projects/lwip)
Content-Length: 98
Content-Type: text/html
{"user": "johndoe", "admin": false, "uid": 1000,
"groups": ["users", "wheel", "audio", "video"]}
可以看到返回結(jié)果符合我們的預(yù)期。不過(guò)Content-Type: text/html和我們想返回的數(shù)據(jù)格式不符合。
在httpd_structs.h中,存在以下定義:
#define HTTP_HDR_HTML HTTP_CONTENT_TYPE("text/html")
#define HTTP_HDR_SSI HTTP_CONTENT_TYPE("text/html\r\nExpires: Fri, 10 Apr 2008 14:00:00 GMT\r\nPragma: no-cache")
#define HTTP_HDR_GIF HTTP_CONTENT_TYPE("image/gif")
#define HTTP_HDR_PNG HTTP_CONTENT_TYPE("image/png")
#define HTTP_HDR_JPG HTTP_CONTENT_TYPE("image/jpeg")
#define HTTP_HDR_BMP HTTP_CONTENT_TYPE("image/bmp")
#define HTTP_HDR_ICO HTTP_CONTENT_TYPE("image/x-icon")
#define HTTP_HDR_APP HTTP_CONTENT_TYPE("application/octet-stream")
#define HTTP_HDR_JS HTTP_CONTENT_TYPE("application/javascript")
#define HTTP_HDR_RA HTTP_CONTENT_TYPE("application/javascript")
#define HTTP_HDR_CSS HTTP_CONTENT_TYPE("text/css")
#define HTTP_HDR_SWF HTTP_CONTENT_TYPE("application/x-shockwave-flash")
#define HTTP_HDR_XML HTTP_CONTENT_TYPE("text/xml")
#define HTTP_HDR_PDF HTTP_CONTENT_TYPE("application/pdf")
#define HTTP_HDR_JSON HTTP_CONTENT_TYPE("application/json")
#define HTTP_HDR_CSV HTTP_CONTENT_TYPE("text/csv")
#define HTTP_HDR_TSV HTTP_CONTENT_TYPE("text/tsv")
#define HTTP_HDR_SVG HTTP_CONTENT_TYPE("image/svg+xml")
#define HTTP_HDR_SVGZ HTTP_CONTENT_TYPE_ENCODING("image/svg+xml", "gzip")
#define HTTP_HDR_DEFAULT_TYPE HTTP_CONTENT_TYPE("text/plain")
要使用這些定義,需要開(kāi)啟
#define LWIP_HTTPD_DYNAMIC_HEADERS 1
以啟動(dòng)動(dòng)態(tài)頭功能。
啟用該功能后,httpd會(huì)在獲取到返回?cái)?shù)據(jù)后,根據(jù)訪問(wèn)的uri來(lái)動(dòng)態(tài)變更頭
#if LWIP_HTTPD_DYNAMIC_HEADERS
/* Determine the HTTP headers to send based on the file extension of
* the requested URI. */
if ((hs->handle == NULL) || ((hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) == 0)) {
get_http_headers(hs, uri);
}
將訪問(wèn)uri變更為json
/* this example only provides one file */
if (!strcmp(name, "/generated.json")) {
可以看到返回?cái)?shù)據(jù)
HTTP/1.0 200 OK
Server: lwIP/2.2.0rc1 (http://savannah.nongnu.org/projects/lwip)
Content-Length: 98
Content-Type: application/json
{"user": "johndoe", "admin": false, "uid": 1000,
"groups": ["users", "wheel", "audio", "video"]}
但是這種方式有個(gè)限制,即GET時(shí)必須確定uri后綴,也算是LWIP的一種限制,在不改動(dòng)lwip的前提下,暫時(shí)無(wú)更好的方法。
如果不加任何后綴,將返回content-type: application/octet-stream,這時(shí)候需要和前端約定好解數(shù)據(jù)的方式。

浙公網(wǎng)安備 33010602011771號(hào)