NetBurner 3.5.6
PDF Version
HTML Form Post

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) {
init(); // Initialize network stack
EnableSystemDiagnostics(); // Enable debug output
StartHttp(); // Start HTTP server on port 80
WaitForActiveNetwork(TICKS_PER_SECOND * 5); // Wait for DHCP connectivity
iprintf("Application: %s\r\nNNDK Revision: %s\r\n", AppName, GetReleaseTag());
// Main application loop - all form processing handled by callbacks
while (1) {
OSTimeDly(TICKS_PER_SECOND);
}
}
#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:
// Initialize processing
break;
case eVariable:
// Process form variable
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:
// Handle file uploads (not used)
break;
case eEndOfPost:
// Send response after processing
RedirectResponse(sock, "page2.html");
break;
}
return 0;
}
void RedirectResponse(int sock, PCSTR new_page)
Redirect a HTTP request to a different page.

Registration:**

HtmlPostVariableListCallback postForm1("form1*", form1PostCallBack);
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:
// Initialize all variables
for (int i = 0; i < numVars; i++) {
strVar[i][0] = '\0';
}
form2ParseError = false;
break;
case eVariable:
// Process each variable with validation
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) {
// Similar processing for Var1, Var2, Var3
} else {
// Unknown variable detected
iprintf("*** Error: Form variable name not found\r\n");
form2ParseError = true;
}
break;
case eEndOfPost:
// Display summary and redirect
iprintf("Variable Summary:\r\n");
for (int i = 0; i < numVars; i++) {
iprintf(" Var%d = \"%s\" \r\n", i, strVar[i]);
}
RedirectResponse(sock, "complete.html");
break;
}
return 0;
}

Dynamic Content Functions

Form 1 Data Display

void WebTextForm1(int sock, PCSTR url) {
fdprintf(sock, "\"%s\"", textForm1);
}
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) {
fdprintf(sock, "<pre>\r\n");
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:

// Safe string copying with explicit size limit
strncpy(textForm1, pValue, varLenForm1 - 1);
textForm1[varLenForm1 - 1] = '\0'; // Ensure null termination

Variable Name Validation

Form 2 includes comprehensive variable validation:

if (strcmp("Var0", pName) == 0) {
// Valid variable - process normally
strncpy(strVar[0], pValue, varLen - 1);
iprintf("strVar0 set to: \"%s\"\r\n", strVar[0]);
} else {
// Invalid variable - set error flag
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

  1. Build the application:
    make clean
    make
  2. Deploy to NetBurner device:
    make load DEVIP=<device_ip_address>
  3. 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 │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
  1. Access main page:
    • Open browser to http://<device_ip>/
    • Verify Form 1 display with NetBurner logo
  2. 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
  3. 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
  4. 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:

// Matches any URL starting with "form1"
HtmlPostVariableListCallback postForm1("form1*", form1PostCallBack);
// Matches any URL starting with "form2"
HtmlPostVariableListCallback postForm2("form2*", form2PostCallBack);

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) {
RedirectResponse(sock, "form_step2.html");
} else if (currentStep == 2) {
RedirectResponse(sock, "complete.html");
}
break;