HtmlFormPost Example
Overview
This NetBurner example demonstrates how to handle HTML form submissions using HTTP POST requests on embedded devices. The application showcases complete form processing workflows including data validation, error handling, and user feedback through a multi-page web interface.
Features
- Interactive HTML form processing with POST request handling
- Multi-page form workflow with user navigation
- Real-time data validation and error reporting
- Persistent data storage during form processing sessions
- Console logging for debugging and monitoring
- Safe string handling with buffer overflow protection
Project Structure
HtmlFormPost/
├── src/
│ ├── main.cpp # Main application and HTTP server startup
│ └── web.cpp # POST request handlers and form processing
├── html/
│ ├── index.html # Form 1 - Single field input
│ ├── page2.html # Form 2 - Multi-field input
│ ├── complete.html # Results page with processed data
│ └── images/
│ └── netburner-logo.gif # NetBurner logo image
└── makefile # Build configuration
Architecture
System Components
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Main Program │ │ POST Handlers │ │ HTML Forms │
│ (main.cpp) │ │ (web.cpp) │ │ (html folder) │
│ │ │ │ │ │
│ - Network Init │ │ - Form1 Handler │ │ - index.html │
│ - HTTP Server │<──>│ - Form2 Handler │<──>│ - page2.html │
│ - Main Loop │ │ - Data Storage │ │ - complete.html │
└──────────────────┘ └──────────────────┘ └──────────────────┘
│ │ │
V V V
┌──────────────────────────────────────────────────────────────────┐
│ NetBurner HTTP Server │
│ Handles POST request routing and processing │
└──────────────────────────────────────────────────────────────────┘
POST Request Processing Flow
Form Submission Flow:
┌──────────┐ ┌───────────────┐ ┌──────────────────┐ ┌──────────┐
│ Browser │ │ HTTP Server │ │ POST Callback │ │ Response │
│ Submit │────>│ (Port 80) │────>│ Handler │────>│ Page │
└──────────┘ └───────────────┘ └──────────────────┘ └──────────┘
│ │ │
│ V │
│ ┌──────────────────┐ │
│ │ Data Processing │ │
│ │ and Validation │ │
│ └──────────────────┘ │
│ │ │
V V V
┌───────────────┐ ┌──────────────────┐ ┌──────────┐
│ URL Pattern │ │ Variable Storage │ │ Redirect │
│ Matching │ │ and Logging │ │ Response │
└───────────────┘ └──────────────────┘ └──────────┘
Application Flow
User Interaction Sequence
User Workflow:
┌───────────┐ ┌────────────┐ ┌────────────┐ ┌─────────────┐
│ Access │ │ Fill Form1 │ │ Fill Form2 │ │ View │
│ index.html│ ──> │ Submit │ ──> │ Submit │ ──> │ Results │
└───────────┘ └────────────┘ └────────────┘ └─────────────┘
│ │ │ │
V V V V
┌───────────┐ ┌────────────┐ ┌────────────┐ ┌─────────────┐
│ Display │ │ Process │ │ Process │ │ Show All │
│ Form1 │ │ textForm1 │ │ 4 Variables│ │ Form Data │
└───────────┘ └────────────┘ └────────────┘ └─────────────┘
Key Components
Main Application (main.cpp)
The main application handles system initialization and HTTP server setup:
void UserMain(void *pd) {
iprintf(
"Application: %s\r\nNNDK Revision: %s\r\n", AppName,
GetReleaseTag());
while (1) {
}
}
#define TICKS_PER_SECOND
System clock ticks per second.
Definition constants.h:49
const char * GetReleaseTag()
Returns the NNDK release tag information.
void StartHttp(uint16_t port, bool RunConfigMirror)
Start the HTTP web server. Further documentation in the Initialization section Initialization - Syste...
void init()
System initialization. Ideally called at the beginning of all applications, since the easiest Recover...
void EnableSystemDiagnostics()
Turn on the diagnostic reports from the config page.
bool WaitForActiveNetwork(uint32_t ticks_to_wait=120 *TICKS_PER_SECOND, int interface=-1)
Wait for an active network connection on at least one interface.
Key Functions:**
- System and network initialization
- HTTP server startup on port 80
- DHCP network configuration with 5-second timeout
- Continuous operation loop
Form Processing System (web.cpp)
Form 1: Single Field Processing
Data Storage:**
const int varLenForm1 = 80;
char textForm1[varLenForm1];
POST Callback Handler:**
int form1PostCallBack(int sock, PostEvents event, const char *pName, const char *pValue) {
switch (event) {
case eStartingPost:
break;
case eVariable:
if (strcmp("textForm1", pName) == 0) {
strncpy(textForm1, pValue, varLenForm1 - 1);
textForm1[varLenForm1 - 1] = '\0';
iprintf("textForm1 set to: \"%s\"\r\n", pValue);
}
break;
case eFile:
break;
case eEndOfPost:
break;
}
return 0;
}
void RedirectResponse(int sock, PCSTR new_page)
Redirect a HTTP request to a different page.
Registration:**
Implements the HtmlPostVariableListHandler class as a function pointer callback for HTTP POST submiss...
Definition httppost.h:131
Form 2: Multi-Field Processing
Data Storage:**
const int numVars = 4;
const int varLen = 80;
char strVar[numVars][varLen];
bool form2ParseError = false;
POST Callback Handler:**
int form2PostCallBack(int sock, PostEvents event, const char *pName, const char *pValue) {
switch (event) {
case eStartingPost:
for (int i = 0; i < numVars; i++) {
strVar[i][0] = '\0';
}
form2ParseError = false;
break;
case eVariable:
if (strcmp("Var0", pName) == 0) {
strncpy(strVar[0], pValue, varLen - 1);
strVar[0][varLen - 1] = '\0';
iprintf("strVar0 set to: \"%s\"\r\n", strVar[0]);
} else if (strcmp("Var1", pName) == 0) {
} else {
iprintf("*** Error: Form variable name not found\r\n");
form2ParseError = true;
}
break;
case eEndOfPost:
iprintf("Variable Summary:\r\n");
for (int i = 0; i < numVars; i++) {
iprintf(" Var%d = \"%s\" \r\n", i, strVar[i]);
}
break;
}
return 0;
}
Dynamic Content Functions
Form 1 Data Display
void WebTextForm1(int sock, PCSTR url) {
}
int fdprintf(int fd, const char *format,...)
Print formatted output to a file descriptor.
Form 2 Data Display with Error Reporting
void WebTextForm2(int sock, PCSTR url) {
for (int i = 0; i < numVars; i++)
fdprintf(sock,
" Var%d = \"%s\" \r\n", i, strVar[i]);
if (form2ParseError)
fdprintf(sock,
"*** Error: Form variable name not found\r\n </pre>");
}
HTML Form Structure
Form 1: Single Field Input (index.html)
<html>
<title>HTML Post Example</title>
<body>
<img src="images/netburner-logo.gif">
<h1><font face="arial">HTML Form Post Example, First Form</font></h1>
<form action="form1" method=post>
<table>
<tr>
<td width=40> </td>
<td>Enter data to post:
<input type="text" name="textForm1" value="<!--CPPCALL WebTextForm1 -->">
</td>
<td><input type="submit" value="Submit"></td>
</tr>
</table>
</form>
</body>
</html>
Form Elements:**
- Action:
form1 - Routes to Form 1 POST handler
- Method:
post - Uses HTTP POST for data transmission
- Input field:
textForm1 - Single text input with preserved value
- Dynamic content:
<!--CPPCALL WebTextForm1 --> - Shows current value
Form 2: Multi-Field Input (page2.html)
<html>
<title>HTML Post Example</title>
<body>
<img src="images/netburner-logo.gif">
<h1><font face=arial>HTML Form Post Example, Second Form</font></h1>
<form action="form2" method=post>
Enter data to post var0: <input type="text" name="Var0" value="Var0"><br><br>
Enter data to post var1: <input type="text" name="Var1" value="Var1"><br><br>
Enter data to post var2: <input type="text" name="Var2" value="Var2"><br><br>
Enter data to post var3: <input type="text" name="Var3" value="Var3"><br><br>
<input type="submit" value="Submit">
</form>
</body>
</html>
Form Elements:**
- Action:
form2 - Routes to Form 2 POST handler
- Method:
post - HTTP POST transmission
- Multiple inputs:
Var0, Var1, Var2, Var3 - Four text fields
- Default values: Pre-populated with variable names for testing
Results Page: Data Display (complete.html)
<html>
<title>HTML Post Example</title>
<body>
<img src="images/netburner-logo.gif">
<h1><font face="arial">HTML Form Post Example, Completion Page</font></h1>
<font face="arial" size=5>Congratulations, Form Processing Complete</font>
Form1 Data: <!--CPPCALL WebTextForm1 -->
Form2 Data:
<!--CPPCALL WebTextForm2 -->
<font face=veranda><a href="index.html">Back to Home Page</a></font>
</body>
</html>
Dynamic Content:**
- Form 1 results:
<!--CPPCALL WebTextForm1 --> - Displays processed Form 1 data
- Form 2 results:
<!--CPPCALL WebTextForm2 --> - Shows all Form 2 variables
- Navigation: Link back to start page for repeated testing
POST Event Processing
Event Types and Sequence
Event Processing Timeline:
┌───────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ eStartingPost │ │ eVariable │ │ eVariable │ │ eEndOfPost │
│ Initialize │ ──> │ Process │ ──> │ Process │ ──> │ Send │
│ Variables │ │ Field 1 │ │ Field N │ │ Response │
└───────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
│ │ │ │
V V V V
┌───────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Clear data │ │ Validate │ │ Validate │ │ Redirect │
│ Reset flags │ │ Store data │ │ Store data │ │ to results │
│ Prepare │ │ Log values │ │ Log values │ │ Generate │
└───────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
eStartingPost
- When: Called at the beginning of form processing
- Purpose: Initialize variables and reset state
- Use cases: Clear previous data, reset error flags, prepare for new submission
eVariable
- When: Called once for each form variable
- Purpose: Process individual form fields
- Parameters: Variable name and value provided
- Use cases: Data validation, storage, format conversion
eFile
- When: Called for file upload fields
- Purpose: Handle uploaded files
- Note: Not used in this example but available for file upload forms
eEndOfPost
- When: Called after all variables processed
- Purpose: Send response to browser
- Use cases: Generate response pages, redirect to results, send confirmations
Data Validation and Error Handling
Buffer Overflow Protection
All string operations use safe copying with size limits:
strncpy(textForm1, pValue, varLenForm1 - 1);
textForm1[varLenForm1 - 1] = '\0';
Variable Name Validation
Form 2 includes comprehensive variable validation:
if (strcmp("Var0", pName) == 0) {
strncpy(strVar[0], pValue, varLen - 1);
iprintf("strVar0 set to: \"%s\"\r\n", strVar[0]);
} else {
iprintf("*** Error: Form variable name not found\r\n");
form2ParseError = true;
}
Error Reporting Channels
Error Reporting Flow:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Detection Point │ │ Logging Channel │ │ Display Channel │
│ │ │ │ │ │
│ - Variable │ --> │ - Console │ --> │ - Web Page │
│ Validation │ │ Output │ │ Display │
│ - Buffer │ │ - Error Flags │ │ - User │
│ Overflow │ │ - Debug Info │ │ Feedback │
└─────────────────┘ └─────────────────┘ └─────────────────┘
Errors are reported through multiple channels:
- Console output: Real-time debugging information
- Error flags: Boolean variables track processing status
- Web display: Error messages shown on result pages
Usage Instructions
Build and Deployment
- Build the application:
- Deploy to NetBurner device:
make load DEVIP=<device_ip_address>
- Monitor console output:
- Connect serial terminal (115200 baud, 8-N-1)
- Observe startup messages and form processing logs
Web Interface Testing
Complete Form Processing Workflow
Testing Flow:
┌─────────────┐ > ┌─────────────┐ > ┌─────────────┐ > ┌─────────────┐
│ Access │ │ Test Form1 │ │ Test Form2 │ │ View │
│ index.html │ │ Submission │ │ Submission │ │ Results │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
│ │ │ │
V V V V
┌─────────────┐ > ┌─────────────┐ > ┌─────────────┐ > ┌─────────────┐
│ Verify │ │ Monitor │ │ Monitor │ │ Check Data │
│ Form1 │ │ Console │ │ Console │ │ Display │
│ Display │ │ Output │ │ Output │ │ & Navigate │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
- Access main page:
- Open browser to
http://<device_ip>/
- Verify Form 1 display with NetBurner logo
- Test Form 1 submission:
- Enter text in "Enter data to post" field
- Click "Submit" button
- Observe console output:
textForm1 set to: "<your_text>"
- Verify redirect to page2.html
- Test Form 2 submission:
- Modify default values in Var0, Var1, Var2, Var3 fields
- Click "Submit" button
- Observe console output for each variable
- Verify redirect to complete.html
- View results page:
- Check Form1 Data display
- Verify Form2 Data shows all four variables
- Test "Back to Home Page" navigation
Expected Console Output
Application Startup
Application: HTML Form Post Example
NNDK Revision: 3.x.x Build xxxx
Form 1 Processing
textForm1 set to: "User entered text"
Form 2 Processing
strVar0 set to: "Modified Var0"
strVar1 set to: "Modified Var1"
strVar2 set to: "Modified Var2"
strVar3 set to: "Modified Var3"
Variable Summary:
Var0 = "Modified Var0"
Var1 = "Modified Var1"
Var2 = "Modified Var2"
Var3 = "Modified Var3"
Error Conditions
Error: Form variable name not found
URL Pattern Matching
The POST handler registration uses wildcard patterns:
Pattern Examples:**
form1 - Exact match
form1.html - With extension
form1/submit - With path
form1?param=value - With query parameters
Technical Implementation
Memory Management
- Static allocation: Fixed-size buffers for predictable memory usage
- Buffer sizing: Appropriate sizes based on expected input lengths
- String safety: Always ensure null termination and bounds checking
Processing Efficiency
- Minimal processing: Fast and efficient callback functions
- Non-blocking: Avoid long operations in POST callbacks
- Resource cleanup: Release resources promptly after processing
Build Configuration
Build Process:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Source │ │ HTML │ │ Compile │ │ Link and │
│ Files │ ──> │ Processing │ ──> │ C++ Code │ ──> │ Deploy │
│ (.cpp) │ │ (comphtml) │ │ (.o files) │ │ (.elf) │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
The makefile handles HTML compilation automatically:
NAME = HtmlFormPost
# Source files
CPP_SRC += src/main.cpp src/web.cpp
# HTML compilation
CPP_SRC += src/htmldata.cpp
CREATEDTARGS += src/htmldata.cpp
src/htmldata.cpp : $(wildcard html/*.*)
comphtml html -osrc/htmldata.cpp
include $(NNDK_ROOT)/make/boilerplate.mk
Troubleshooting
Common Issues
Troubleshooting Matrix:
┌─────────────────────┬─────────────────────┬─────────────────────┐
│ Symptom │ Possible Cause │ Solution │
├─────────────────────┼─────────────────────┼─────────────────────┤
│ Form not submitting │ Action URL mismatch │ Check pattern match │
│ Data not appearing │ Variable name error │ Verify exact names │
│ Redirect not working│ Missing eEndOfPost │ Check callback │
│ Console output empty│ Diagnostics disabled│ Enable diagnostics │
└─────────────────────┴─────────────────────┴─────────────────────┘
Form Not Submitting
Symptoms:** Form submission doesn't trigger callback Solutions:**
- Verify form action URL matches registered pattern
- Check HTTP method is set to "post"
- Confirm POST handler is registered properly
- Monitor console for error messages
Data Not Appearing
Symptoms:** Form data not stored or displayed Solutions:**
- Verify variable names match exactly between HTML and callback
- Check case sensitivity of variable names
- Ensure proper string copying and null termination
- Monitor console output for processing messages
Redirect Not Working
Symptoms:** Browser doesn't redirect after form submission Solutions:**
- Verify target page exists and is accessible
- Check RedirectResponse() is called in eEndOfPost event
- Ensure callback returns 0 for successful processing
- Test with different browsers
Dependencies
Required NetBurner system libraries:
init.h - System initialization
nbrtos.h - NetBurner RTOS functions
http.h - HTTP server support
httppost.h - POST request handling
fdprintf.h - File descriptor printf functions
iosys.h - I/O system functions
string.h - String manipulation functions
Learning Objectives
This example teaches:
- HTTP POST processing: Complete form submission handling workflows
- Event-driven programming: Using callbacks for asynchronous form processing
- Data validation patterns: Input validation and error handling techniques
- Web application workflows: Multi-page form processing sequences
- String handling safety: Buffer overflow protection and safe string operations
- User interface design: Creating intuitive web forms and feedback systems
Advanced Usage
Custom Validation Logic
Extend form processing with custom validation:
case eVariable:
if (strcmp("email", pName) == 0) {
if (validateEmail(pValue)) {
strncpy(emailField, pValue, EMAIL_LEN - 1);
emailField[EMAIL_LEN - 1] = '\0';
} else {
validationError = true;
}
}
break;
Multi-Step Form Processing
Multi-Step Workflow:
┌─────────────┐ > ┌─────────────┐ > ┌─────────────┐ > ┌─────────────┐
│ Step 1 │ │ Step 2 │ │ Step 3 │ │ Complete │
│ Basic Info │ │ Details │ │ Validation │ │ Results │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
Implement complex workflows:
case eEndOfPost:
if (currentStep == 1) {
} else if (currentStep == 2) {
}
break;