Skip to content

Latest commit

 

History

History
553 lines (472 loc) · 29.4 KB

File metadata and controls

553 lines (472 loc) · 29.4 KB

straight-httpd

Simple httpd solution for embedded systems based on lwip(Raw/TCP) and mbedtls (本项目是基于 lwip 和 mbedtls 开发的 http server 原型).
The goal of this project is to build a prototype system to simulate web services on an embedded system. The simulator has the following benefits (本项目的目标是搭建一个原型系统来模拟嵌入式系统上的 web 服务,利用该模拟器有如下好处):

  • The Windows platform is far more powerful than any MCU, and Microsoft Visual Studio has powerful debugging tools, so debugging will be much more efficient (Windows平台远比任何MCU强大,加上 Visual Studio 丰富的调试手段,让调试效率大大提高).
  • When working with a team, if only limited hardware or J-TAG emulators are available, some features that do not depend on the hardware can be developed simultaneously using this prototype (团队开发时,如果硬件环境不足、或J-TAG仿真器数量有限时,不依赖硬件环境的某些应用可以进行同步开发).
  • lwip and mbedtls have a lot of optional parameters, which need to be optimized for different embedded systems. To compare parameters, extensive experimentation is required, and this prototype can help speed up this process (lwipmbedtls 有非常多的可选参数,如何优化参数以适应不同的嵌入式系统,需要大量的比较实验,该模拟环境可以大大缩减实验周期).

Objective

In a Windows environment, create a Virtual Device (VD) using pcap, lwip and mbedtls, and implement a simple but typical web server.

stack

This project creates a Virtual Device on Computer B. Since the browser on Computer B cannot access this local Virtual Device, another Computer A should be used to access this device remotely.

  • Computer B MUST be a Windows device with VS2010 or later installed. Use VS2010 to open the project straight-httpd/straight-httpd.sln and run it in debug.

  • Computer A could be a Windows, Linux, or a mobile device. For Windows, open File Explorer and search for network neighbors. There should be a device named Virtual Device. Right click on it and select Properties, and you will see the device information, including its IP address and URL. Use default user admin and password password to sign into the device home page.

    discover

Features

  • Support SSDP protocol: neighbours can detect this virtual device in the network.
  • Support HTTPS: all HTTP requests will be redirected to HTTPS (except SSDP XML). Both GET and POST are supported.
  • Support HTTP/1.1 pipelining and keep-alive. It is better for HTTPS to reuse connections because creating new connections is time consuming.
  • Support chunked response: Because the device memory is limited, generally it is not possible to wait for all the data to be ready before replying. When the device begins to return data, it does not know the total length. Transfer-Encoding: chunked allows you to split the data into chunks of various lengths, so that every chunk is within the device’s memory limit.
  • Support range request: Range: bytes=0- allows you to specify a single range. This feature is suitable for paged data, multi-threaded downloading or video fast forwarding.
  • Support request header If-Modified-Since for browsers to cache static files.
  • Support simple SSI (“Server Side Includes”) with simple syntax: <!--#TAG_NAME-->. Note that the syntax does not allow spaces between the "<" and ">". Every tag is a placeholder, which corresponds to a variable. The variable can be HTML content or the initial value of a javascript variable. When the device replies to a request, it will first convert the variable into a string. This string then replaces the corresponding tag. Based on the tag content, the js code can then complete HTML/DOM operations, such as select, checkbox, and radio control etc.
  • Demo pages include:
    • Sign in/out: verify user's username/password, if successful, redirect to "Home", otherwise stay in the login page, the session has 3 minutes timeout after login.
    • Home page: show some device information and the current status of the device, and scan the QR code to access the device web.
    • Upload page: multiple files can be selected and queued to be uploaded to a specific device directory. The concurrency is 1, and every upload process can be terminated separately. Support large files.
    • Files page: you can browse all the files in the specified device directory, enter subdirectories or return to the parent directory. In addition, all files could be downloaded. Support large files.
    • Form page: demonstrates parameter modification and saving.

Future work

Prerequisites

  • Computer A: only need a browser installed. If using the latest npcap driver (v0.9995), the browser on Computer B can access the Virtual Device right now, so Computer A may not be needed.
  • Computer B: need to install wpcap driver, or simply install Wireshark that includes wpcap driver.

Dependencies

  • folder pcap: wpcap C library copied from the original location: pcap
  • folder lwip: copied from the original location: git://git.savannah.nongnu.org/lwip.git
  • folder mbedtls: copied from the original location: mbedtls. The file config.h has been modified.
  • QR-Code-generator by nayuki, copied from the original location: source

Project folders

  • folder lwip-port: lwip porting files on Windows platform

    • Receive ethernet frame from wpcap and send it to lwip stack.
    • Send ethernet frame from lwip stack via wpcap.
    • Also provide features: Mutex, Semaphore, Mailbox, System Tick, and File API.
  • folder straight-httpd: Microsoft Visual Studio 2010 project: straight-httpd.sln, and the project straight-buildfs can package all web pages and convert them into source code.

Self-signed certificate for HTTPS

  • The simplest way is to use the tool EmbedTools, which can generate the pem file named lwip_cert.c.
  • If you want to create your customized certificate, run makecert.bat. Both the certificate and the private key will be saved in the file server.pfx.
  • Use OpenSSL or other tools to convert PFX file to PEM format.
   openssl pkcs12 -in server.pfx -out server.pem
  • Copy the following content in the pem file to http_api.c
const char *privkey_pass = "straight";
const char *privkey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\n"\
"MIIJjjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIh7VHf699+v0CAggA\n"\
    。。。。
"-----END ENCRYPTED PRIVATE KEY-----\n";

const char *cert = "-----BEGIN CERTIFICATE-----\n"\
"MIIFEDCCAvigAwIBAgIQ+UjKCIcDNLRKcHf+9tlCFjANBgkqhkiG9w0BAQ0FADAR\n"\
    。。。。
"-----END CERTIFICATE-----\n";

Task / Thread

  • LWIP task can be thought as a black-box that is asynchronously driven by the network events.

  • Immediate response is very important for high throughput.

  • This task need access various data in local database or variables which should be protected for concurrency.

    tasks

Modifications

  • /src/include/lwip/priv/tcpip_priv.h
ETHNET_INPUT //2020-03-29 added by straight coder
  • /src/include/lwip/arch.h
define LWIP_NO_STDINT_H 1	//2020-03-29 set to 1 by straight coder
  • /src/core/tcp.c
void tcp_kill_all(void)  //2020-03-29 new function by straight coder
  • /src/core/ipv4/dhcp.c
OnDhcpFinished();   //2020-03-29 notify when IP is ready by straight coder
  • /src/api/tcpip.c
sys_mbox_t tcpip_mbox; //2020-03-29 removed static by straight coder	
extern struct netif main_netif; //2020-03-29 added by straight coder
extern err_t ethernetif_input(struct netif *netif); //2020-03-29 added by straight coder
void tcpip_thread_handle_msg(struct tcpip_msg *msg); //2020-03-29 removed static by straight coder
	
case ETHNET_INPUT: //2020-03-29 added by straight coder
   ethernetif_input(&main_netif);
   break;
  • for pcap
#define ETH_PAD_SIZE  0 
struct member alignment 1 byte(/Zp1)

Structure for CGI mapping (定义请求路径、http接收事件与处理函数之间的绑定关系)

cgi-mapping

    struct CGI_Mapping
    {
	char path[MAX_CGI_PATH];	//string, request full path
	unsigned long optionsAllowed;	//defined by CGI_OPT_xxxxxx

	void (*OnCancel)(REQUEST_CONTEXT* context);

	//request callbacks
	int  (*OnHeaderReceived)(REQUEST_CONTEXT* context, char* header_line); //return 1 if eaten
	void (*OnHeadersReceived)(REQUEST_CONTEXT* context);
	int  (*OnContentReceived)(REQUEST_CONTEXT* context, char* buffer, int size);
	void (*OnRequestReceived)(REQUEST_CONTEXT* context);
	
	//response callbacks
	void (*SetResponseHeaders)(REQUEST_CONTEXT* context, char* HttpCode);
	int  (*ReadContent)(REQUEST_CONTEXT* context, char* buffer, int maxSize);
	void (*OnAllSent)(REQUEST_CONTEXT* context);
	
	void (*OnFinished)(REQUEST_CONTEXT* context);
	
	struct CGI_Mapping* next;
    };
    //其中的回调函数,如果不需要可以设置为 NULL,如:静态网页、SSI等已被http_core.c处理,就不需要额外处理
    //像 POST 等需要特殊处理的,就需要增加相应的回调函数
  • Functions for CGI processing
    //setup mapping when initializing httpd context
    //建立所有的路径、事件和函数的绑定关系
    void CGI_SetupMapping(void); 
   
    //cancel notification to app layer because of any HTTP fatal errors
    //  including timeout, format errors, sending failures, and stack keneral errors
    //如果错误源自系统内部,如处理http时遇到内存不足、或连接错误等无法恢复的错误时,通过回调通知上层做相应处理(可选)
    void CGI_Cancel(REQUEST_CONTEXT* context);

    //called by FreeHttpContext()
    //单次请求结束之前,回调通知上层做相应处理(可选)
    void CGI_Finish(REQUEST_CONTEXT* context);

    //called when the first line of the HTTP request is completely received
    //收到http请求路径时,立即查找相关的绑定设置,只有CGI_SetupMapping定义过的绑定请求才会被处理
    void CGI_SetCgiHandler(REQUEST_CONTEXT* context);

    //if any HTTP request header/line is skipped by http-core.c, the following function will be called, return 1 if accepted
    //如果请求头没有被http-core.c吃掉,就要看上层有没有定义回调函数以进行特殊处理
    int CGI_HeaderReceived(REQUEST_CONTEXT* context, char* header_line);

    //called when all HTTP request headers/lines are received
    //所有请求头接收完毕,在此回调上层函数可以进行一些额外准备,以便消化紧接着的请求内容(如POST,或上载文件)
    void CGI_HeadersReceived(REQUEST_CONTEXT* context);

    //called when any bytes of the HTTP request body are received (may be partial), and return the accepted count, 0 for blocking, or negative for error.
    //上传内容收到后,回调上层函数,告知数据块位置和大小,经上层处理后,返回实际消化掉的字节数,0表示阻塞,负数表示接收遇错会导致连接关闭。
    //如果回调只是消化了部分数据,那残余数据会被后续的数据继续触发、或被定时触发
    int CGI_ContentReceived(REQUEST_CONTEXT* context, char* buffer, int size);

    //called when the HTTP request body is completely received
    //所有请求内容完全收到,且缓存内容被完全处理,下一步应该进入应答阶段
    void CGI_RequestReceived(REQUEST_CONTEXT* context);

    //set response headers: content-type, content-length, connection, and http code and status info
    //响应请求的第一步是准备响应头,如填写响应数据类型、长度、错误码、或连接保持参数
    void CGI_SetResponseHeaders(REQUEST_CONTEXT* context, char* HttpCodeInfo);

    //load response body chunk by chunk
    //  there are two-level progresses initialized as 0: 
    // 	  context->ctxResponse._dwOperStage = 0; //major progress, set _dwOperStage=STAGE_END if it is the last data chunk
    //	  context->ctxResponse._dwOperIndex = 0; //sub-progress for each stage, for app layer internal use
    //    caller: called from HTTP_PROC_CALLER_RECV (event) / HTTP_PROC_CALLER_POLL (timer) / HTTP_PROC_CALLER_SENT (event)
    // int (*ReadContent)(REQUEST_CONTEXT* context, char* buffer, int maxSize); //need to be implemented by upper level
    //  return 1 if data is ready to send
    //	  data MUST be put in context->ctxResponse._sendBuffer, 
    //         and the buffer size set to context->ctxResponse._bytesLeft before return
    //来自lwip的OnReceive和OnPoll会触发该接口的调用,上层需要提供绑定函数ReadContent(REQUEST_CONTEXT* context, char* buffer, int maxSize)
    //该函数从上层获取最多maxSize字节数据用于发送(返回值为实际可发送数量),具体发送进度控制需要在回调ReadContent中实现:
    //请求上下文中有两个变量可用于进度控制:
    //    主进度变量_dwOperStage:如果是最后一块数据,调用结束时置为STAGE_END,其他值由用户控制
    //    辅进度变量_dwOperIndex:如果需要用到两级进度、或仅仅是做个判断标记,该变量也用得上
    int CGI_LoadContentToSend(REQUEST_CONTEXT* context, int caller);

GET handling

Device HTTP Events CGI Adapter - Actions Description
GET /app/index.shtml HTTP/1.1 CGI_SetCgiHandler(context)
查找绑定设置,找到处理函数
First request line
Connection: keep-alive
Cookie: SID=0123ABCD
X-Auth-Token: SID=0123ABCD
Range: bytes=0-
If-Modified-Since:
processed in http_core.c
默认处理一些解析需要的请求头
Request headers
other headers CGI_HeaderReceived(context,line)
不关心的请求头留给回调处理
Request headers
all headers received Check session: GetSession(char* token)
找到已登录的会话
CGI_HeadersReceived(context)
End of the request headers
request completely received CGI_RequestReceived(context) Ready to response
send response headers CGI_SetResponseHeaders(context,codeInfo) Response headers
response with content CGI_LoadContentToSend(context, caller) Response body
Response completely sent out OnAllSent(context) Response finished
When connection disconnected or -500 CGI_Finish(context) For additional cleanup
lwip error, request error, timeout, <=-400 CGI_Cancel(context) For additional cleanup

POST handling

Device HTTP Events CGI Adapter - Actions Description
POST /auth/login.html HTTP/1.1 CGI_SetCgiHandler(context) First request line
Content-Type: application/x-form-urlencoded
Content-Length: xxx
Cookie: SID=0123ABCD
X-Auth-Token: SID=0123ABCD
Connection: keep-alive
processed in http_core.c Request headers
other headers CGI_HeaderReceived(context,line) Request headers
all headers received Check session: GetSession(char* token)
CGI_HeadersReceived(context)
End of the request headers
request body CGI_ContentReceived(context, buffer, size) Request body
post body received CGI_RequestReceived(context) Ready to response
send response headers CGI_SetResponseHeaders(context,codeInfo) Response headers
response with content CGI_LoadContentToSend(context, caller) Response body
Response completely sent out OnAllSent(context) Response finished
When connection disconnected or -500 CGI_Finish(context) For additional cleanup
lwip error, request error, timeout, <=-400 CGI_Cancel(context) For additional cleanup

Performance Tuning

  • MEM_SIZE in file lwipopts.h, large memory for more connections.
#if (ENABLE_HTTPS > 0)
#define MEM_SIZE  (130*1024) //for Chrome, at least 130kB 
#else
#define MEM_SIZE  (24*1024)  //for HTTP only
#endif
  • TCP_MSS in file lwipopts.h, the bigger the TCP_MSS, the higher the throughput, but the more the memory.
#define TCP_MSS   800 //for HTTPS, at least 800; for HTTP can be smaller 
  • TCP_WND in file lwipopts.h
#if (ENABLE_HTTPS > 0)
#define TCP_WND  (22 * TCP_MSS) //for HTTPS, should be greater than MBEDTLS_SSL_MAX_CONTENT_LEN
#else
#define TCP_WND  (4 * TCP_MSS) //for HTTP, default value is OK
#endif
  • MAX_CONNECTIONS in http_core.h
#define MAX_CONNECTIONS  4 //max concurrent tcp connections, some browsers may use up to 4 connections for one session.
  • MAX_REQ_BUF_SIZE in http_core.h, the larger the receiving buffer, the faster the uploading speed.
#define MAX_REQ_BUF_SIZE TCP_MSS //max. length of the header that can be recognized, and it must be bigger than the max.length of path.
  • MAX_SEND_BUF_SIZE in http_core.h, the larger sending buffer, the faster the downloading speed.
#define MAX_SEND_BUF_SIZE 2*TCP_MSS

Example for SSDP response

  • cgi_ssdp.c processes SSDP requests.
  • All information tags can be modified using API functions. All tags are defined and parsed by cgi_ssi.c.
    e.g. GetDeviceName(), GetVendor(), GetModel(), GetDeviceUUID(). 

discover-prop

Example for sign-in UI

  • This example applies to devices that require login or exclusive use.
  • /auth/login.html is the default page before authentication. The user must provide the username and password. All web pages are physically located in the folder defined by LOCAL_WEBROOT.
#define LOCAL_WEBROOT	"D:/straight/straight-httpd/straight-httpd/straight-httpd/httpd/cncweb/"
  • cgi_auth.c processes the user’s sign-in/sign-out. This module responds to all requests with the prefix /auth/*.
  • /auth/session_check.cgi checks if the current session has timed out.
  • /auth/logout.cgi signs out the user and frees the session context.
  • Default user: admin, password: password
  • CGI mapping:
struct CGI_Mapping g_cgiAuth = {
	"/auth/*", //char* path;
	CGI_OPT_GET_ENABLED | CGI_OPT_POST_ENABLED,//Options ===> GET login page, then POST username/password;

	NULL, //void (*OnCancel)(REQUEST_CONTEXT* context);
	NULL, //int (*OnHeaderReceived)(REQUEST_CONTEXT* context, char* header_line);
	
	Auth_OnHeaders, //void (*OnHeadersReceived)(REQUEST_CONTEXT* context); ===> prepare buffer to receive username/password
	Auth_OnPostData, //int  (*OnContentReceived)(REQUEST_CONTEXT* context, char* buffer, int size); ===> receive posted data (username/password)
	Auth_OnRequestReceived, //void (*OnRequestReceived)(REQUEST_CONTEXT* context); ===> POST: check username and password, create session and cookie; 
	                                                                               ===> GET: check session timeout;
	                                                                               ===> GET: logout and kill session;
	Auth_SetResponseHeaders, //void (*SetResponseHeader)(REQUEST_CONTEXT* context, char* HttpCode); ===> response with session cookie
	WEB_ReadContent, //int  (*ReadContent)(REQUEST_CONTEXT* context, char* buffer, int maxSize); ===> default processing
	WEB_AllSent, //int  (*OnAllSent)(REQUEST_CONTEXT* context);                                  ===> default processing
	WEB_Finished, //void (*OnFinished)(REQUEST_CONTEXT* context);                                ===> default processing

	NULL //struct CGI_Mapping* next;
};
    int CheckUser(char* u, char* p);

signin

Example for status and information

  • This example is applicable to all static or SSI (with extensions: .shtml;.shtm;.ssi) web pages.
  • /app/index.shtml is the home page after authentication.
  • cgi_web.c responds to all requests with prefix /app/* after authentication.
  • CGI mapping:
struct CGI_Mapping g_cgiWebApp = {
	"/app/*", //char* path;
	CGI_OPT_AUTH_REQUIRED | CGI_OPT_GET_ENABLED,// unsigned long options; ===> Login to access; GET only
	NULL, //void (*OnCancel)(REQUEST_CONTEXT* context);
	NULL, //int (*OnHeaderReceived)(REQUEST_CONTEXT* context, char* header_line);
	NULL, //void (*OnHeadersReceived)(REQUEST_CONTEXT* context);
	NULL, //int  (*OnContentReceived)(REQUEST_CONTEXT* context, char* buffer, int size);
	WEB_RequestReceived, //void (*OnRequestReceived)(REQUEST_CONTEXT* context); ===> check request (SSI? gzip? chunked? modified?) and prepare to response
	
	WEB_AppendHeaders, //void (*SetResponseHeader)(REQUEST_CONTEXT* context, char* HttpCode); ===> set response headers (gzip? chunked? Last-Modified?)
	WEB_ReadContent, //int  (*ReadContent)(REQUEST_CONTEXT* context, char* buffer, int maxSize); ===> replace SSI and send response block by block or chunk by chunk
	
	WEB_AllSent, //int  (*OnAllSent)(REQUEST_CONTEXT* context); ===> close file
	WEB_Finished, //void (*OnFinished)(REQUEST_CONTEXT* context); ===> close file
	
	NULL //struct CGI_Mapping* next;
};
  • cgi_ssi.c includes all infomation getters.
    char* GetVendor(void);
    char* GetVendorURL(void);
    char* GetModel(void);
    char* GetModelURL(void);
    char* GetDeviceName(void);
    char* GetDeviceSN(void);
    char* GetDeviceUUID(void);
    char* GetDeviceVersion(void);
    int FillMAC(void* context, char* buffer, int maxSize);
    
    static SSI_Tag g_Getters[] = {
	{ "DEV_VENDOR",		TAG_GETTER, GetVendor },
	{ "DEV_VENDOR_URL",	TAG_GETTER, GetVendorURL },
	{ "DEV_MODEL",		TAG_GETTER, GetModel },
	{ "DEV_MODEL_URL",	TAG_GETTER, GetModelURL },
	{ "DEV_MAC",		TAG_PROVIDER, FillMAC },
	{ "DEV_NAME",		TAG_GETTER, GetDeviceName },
	{ "DEV_SN",		TAG_GETTER, GetDeviceSN },
	{ "DEV_UUID",		TAG_GETTER, GetDeviceUUID },
	{ "DEV_VERSION",	TAG_GETTER, GetDeviceVersion },
	{ NULL, NULL, NULL }
    };

info

Example for uploading

  • This example is applicable to device firmware upgrade.
  • /app/upload.shtml and /app/plugin/fileTransfer/fileTransfer.js provide a demo for uploading files. The destination folder is defined by UPLOAD_TO_FOLDER.
#define UPLOAD_TO_FOLDER "D:/straight/straight-httpd/straight-httpd/straight-httpd/httpd/cncweb/app/cache/"
  • cgi_upload.c responds to the request URL /api/upload.cgi.
  • CGI mapping
struct CGI_Mapping g_cgiUpload = {
	"/api/upload.cgi",
	CGI_OPT_AUTH_REQUIRED | CGI_OPT_POST_ENABLED,// unsigned long options; ===> Login to access; POST only

	Upload_OnCancel, //void (*OnCancel)(REQUEST_CONTEXT* context); ===> callback when connection is down, clean the uploaded data or file

	Upload_OnHeaderReceived, //int (*OnHeaderReceived)(REQUEST_CONTEXT* context, char* header_line); ===> special header processing
	Upload_OnHeadersReceived, //void (*OnHeadersReceived)(REQUEST_CONTEXT* context); ===> request received, and check condition: busy? oversized?
	Upload_OnContentReceived, //int  (*OnContentReceived)(REQUEST_CONTEXT* context, char* buffer, int size); ===> receive and save data block by block with flow control
	Upload_AllReceived, //void (*OnRequestReceived)(REQUEST_CONTEXT* context); ===> all post data received, close file

	NULL, //void (*SetResponseHeader)(REQUEST_CONTEXT* context, char* HttpCode);
	NULL, //int  (*ReadContent)(REQUEST_CONTEXT* context, char* buffer, int maxSize);
	
	NULL, //int  (*OnAllSent)(REQUEST_CONTEXT* context);
	NULL, //void (*OnFinished)(REQUEST_CONTEXT* context);
	
	NULL //struct CGI_Mapping* next;
};

long Upload_Start(void* context, char* szFileName, long nFileSize);
long Upload_GetFreeSize(unsigned long askForSize);
long Upload_Received(REQUEST_CONTEXT* context, unsigned char* pData, unsigned long dwLen);
void Upload_AllReceived(REQUEST_CONTEXT* context);

upload

Example for file explorer

  • This is a good example for RESTful API using chunked header.
  • /app/files.shtml and /app/plugin/fileList/fileList.js provide a demo for file browsing. The directory is defined by FOLDER_TO_LIST.
#define FOLDER_TO_LIST	"D:/straight/straight-httpd/straight-httpd/straight-httpd/httpd/cncweb/app/cache/"
  • cgi_files.c responds to the request with URL /api/files.cgi.
  • CGI mapping
struct CGI_Mapping g_cgiFiles = {
	"/api/files.cgi",
	CGI_OPT_AUTH_REQUIRED | CGI_OPT_GET_ENABLED,// unsigned long options; ===> Login to access; GET only

	NULL, //void (*OnCancel)(REQUEST_CONTEXT* context);
	NULL, //int (*OnHeaderReceived)(REQUEST_CONTEXT* context, char* header_line);
	NULL, //void (*OnHeadersReceived)(REQUEST_CONTEXT* context);
	NULL, //int  (*OnContentReceived)(REQUEST_CONTEXT* context, char* buffer, int size);
	Files_OnRequestReceived, //void (*OnRequestReceived)(REQUEST_CONTEXT* context);
	
	Files_SetResponseHeader, //void (*SetResponseHeader)(REQUEST_CONTEXT* context, char* HttpCode);
	Files_ReadOneFileInfo, //int  (*ReadContent)(REQUEST_CONTEXT* context, char* buffer, int maxSize);
	
	NULL, //int  (*OnAllSent)(REQUEST_CONTEXT* context);
	NULL, //void (*OnFinished)(REQUEST_CONTEXT* context);
	
	NULL //struct CGI_Mapping* next;
};
  • Make preparations before response.
   static void Files_OnRequestReceived(REQUEST_CONTEXT* context);
  • Generate response header with chunked header.
   static void Files_SetResponseHeader(REQUEST_CONTEXT* context, char* HttpCodeInfo);
  • Generate one small chunk on each call.
    • static int Files_ReadOneFileInfo(REQUEST_CONTEXT* context, char* buffer, int maxSize);
    • This function is a callback from lwip stack when it is ready to send the next chunk.
    • There are two variables in the context that could be used for the progress control.
      • context->ctxResponse._dwOperStage: It is the first level progress (0-based), and STAGE_END stands for the last chunk.
      • context->ctxResponse._dwOperIndex: Optional, it is the second level progress (0-based), and the max value is context->ctxResponse._dwTotal.
request:
  http://192.168.5.58/api/files.cgi?path=/
response with JSON including 9 chunks:
  {
    "result":1, 
    "data":[
      {"folder":1,"name":"..","size":0,"date":1593903275},
      {"folder":0,"name":"00108.MTS","size":147062784,"date":1596318548},
      {"folder":0,"name":"Building a 3d Printer From Cd Drives -- Only $45.mp4","size":90868943,"date":1595116269},
      {"folder":1,"name":"cfg","size":0,"date":1593903275},
      {"folder":1,"name":"include","size":0,"date":1593903276},
      {"folder":1,"name":"lib","size":0,"date":1593903276},
      {"folder":0,"name":"videoplayback.mp4","size":25542302,"date":1593903614}
    ]
  }

files

Example for configuration form

  • This is a typical form example: GET the existing settings then POST the modifications to device.
  • /app/form.shtml is a form page for modifying parameters. All parameters are processed by cgi_ssi.c.
  • cgi_form.c provides general processing for all forms. All parameters and types are defined in cgi_ssi.c.
  • CGI mapping
struct CGI_Mapping g_cgiForm = {
	"/app/form.shtml", //char* path;
	CGI_OPT_AUTH_REQUIRED | CGI_OPT_GET_ENABLED | CGI_OPT_POST_ENABLED,// unsigned long options; ===> enable GET and POST, login required

	NULL, //void (*OnCancel)(REQUEST_CONTEXT* context);

	NULL, //int (*OnHeaderReceived)(REQUEST_CONTEXT* context, char* header_line);
	Form_OnHeadersReceived, //void (*OnHeadersReceived)(REQUEST_CONTEXT* context); ===> prepare for the following getting or setting
	WEB_OnFormReceived, //int  (*OnContentReceived)(REQUEST_CONTEXT* context, char* buffer, int size);  ===> default processing for POSTed data
	Form_OnRequestReceived, //void (*OnRequestReceived)(REQUEST_CONTEXT* context); ===> apply the POSTed modification

	Form_SetResponseHeaders, //void (*SetResponseHeader)(REQUEST_CONTEXT* context, char* HttpCode);  ===> just call default processing if no further processing
	WEB_ReadContent, //int  (*ReadContent)(REQUEST_CONTEXT* context, char* buffer, int maxSize);  ===> default processing
	Form_AllSent, //int  (*OnAllSent)(REQUEST_CONTEXT* context);  ===> just call default processing WEB_AllSent() if no further processing
	NULL, //void (*OnFinished)(REQUEST_CONTEXT* context);

	NULL //struct CGI_Mapping* next;
};
  • cgi_ssi.c includes all infomation getters and setters.
    static SSI_Tag g_Getters[] = {
	{ "DEV_DHCP",		TAG_PROVIDER, FillDhcp },
	{ "DEV_IP",		TAG_PROVIDER, FillIP },
	{ "DEV_GATEWAY",	TAG_PROVIDER, FillGateway },
	{ "DEV_SUBNET",		TAG_PROVIDER, FillSubnet },
	{ "VAR_SESSION_TIMEOUT",TAG_PROVIDER, FillSessionTimeout },
	{ "VAR_LOCATION",	TAG_GETTER, GetLocation },
	{ "VAR_COLOR",		TAG_GETTER, GetColor },
	{ "VAR_DATE",		TAG_GETTER, GetDate },
	{ "VAR_FONT",		TAG_GETTER, GetFont },
	{ "VAR_LOG",		TAG_PROVIDER, FillLog },
	{ NULL, NULL, NULL }
    };

    static SSI_Tag g_Setters[] = {
	{ "DEV_DHCP",		TAG_SETTER, SetDhcpEnabled },
	{ "DEV_IP",		TAG_SETTER, SetMyIP },
	{ "DEV_GATEWAY",	TAG_SETTER, SetGateway },
	{ "DEV_SUBNET",		TAG_SETTER, SetSubnet },
	{ "VAR_SESSION_TIMEOUT",TAG_SETTER, SetSessionTimeout },
	{ "VAR_LOCATION",	TAG_SETTER, SetLocation },
	{ "VAR_COLOR",		TAG_SETTER, SetColor },
	{ "VAR_DATE",		TAG_SETTER, SetDate },
	{ "VAR_FONT",		TAG_SETTER, SetFont },
	{ "VAR_LOG",		TAG_SETTER, SetLog },
	{ NULL, NULL, NULL }
    };

    void LoadConfig4Edit();
    void ApplyConfig();

form