Merge branch 'master' of https://github.com/kenbarbour/HttpServer
commit
4db11a9318
2
Makefile
2
Makefile
|
@ -3,7 +3,7 @@ MODULES= src test
|
|||
SOURCES ?= $(wildcard $(addsuffix /*.cpp, $(MODULES)))
|
||||
OBJECTS := $(addsuffix .o, $(addprefix .build/, $(basename $(SOURCES))))
|
||||
DEPFILES := $(subst .o,.dep, $(subst .build/,.deps/, $(OBJECTS)))
|
||||
TESTCPPFLAGS = -D_TEST_ $(addprefix -I, $(MODULES)) -Iarduino -g
|
||||
TESTCPPFLAGS = -D_TEST_ $(addprefix -I, $(MODULES)) -g
|
||||
CPPDEPFLAGS = -MMD -MP -MF .deps/$(basename $<).dep
|
||||
TEST_TARGET=run_tests
|
||||
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
/**
|
||||
* Basic WebKernel Web Application Example
|
||||
* In this example, the ESP8266 acts as a standalone access point and allows
|
||||
* the user to control the builtin LED pin over the web.
|
||||
*
|
||||
* To see this example working:
|
||||
* 1. Open the Serial Monitor
|
||||
* 2. Upload to the ESP8266
|
||||
* 3. Note the WiFi SSID, password, and IP Address in the serial monitor
|
||||
* 4. Connect to the newly created WiFi Access Point
|
||||
* 5. Using the web browser or curl, control the LED pin with the following
|
||||
* URLs: (assuming the default configuration)
|
||||
*
|
||||
* * Show the LED status: http://192.168.4.1/led
|
||||
* * Toggle the LED; http://192.168.4.1/led/toggle
|
||||
*/
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <WebKernel.h>
|
||||
|
||||
const char* network_ssid = "ESPThing"; // Name of the WiFi Access Point to create
|
||||
const char* network_password = ""; // Password for WiFi (empty is no password)
|
||||
|
||||
const uint8_t ledPin = LED_BUILTIN; // Arduino Pin controlling the LED
|
||||
|
||||
uint8_t content_buffer[64] = {}; // global buffer to contain response data
|
||||
Buffer content(content_buffer, // print response data to this variable
|
||||
sizeof(content_buffer)/sizeof(content_buffer[0]));
|
||||
|
||||
|
||||
/**
|
||||
* Handle requests for the site index (URL: /)
|
||||
*/
|
||||
void index(HttpRequest& request, HttpResponse& response)
|
||||
{
|
||||
response.headers.set("Content-Type","text/plain");
|
||||
content.println("Site Index");
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle requests to show the LED status
|
||||
* This function should print the status of the LED pin to content
|
||||
*/
|
||||
void show_led(HttpRequest& request, HttpResponse& response)
|
||||
{
|
||||
response.headers.set("Content-Type","text/plain");
|
||||
if (digitalRead(ledPin))
|
||||
content.println("off");
|
||||
else content.println("on");
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle requests to toggle the LED state
|
||||
* Afterwards, send the user the LED's status
|
||||
*/
|
||||
void toggle_led(HttpRequest& request, HttpResponse& response)
|
||||
{
|
||||
digitalWrite(ledPin, !(digitalRead(ledPin)));
|
||||
show_led(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Before any request is handled, clear the content buffer from the last
|
||||
* request and setup the buffer for the new request.
|
||||
*/
|
||||
void init_webkernel(HttpRequest& request, HttpResponse& response)
|
||||
{
|
||||
content.clear(); // clear previously printed content
|
||||
response.content = &content; // attach content buffer to the response
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup a WiFi connection
|
||||
* Called during setup routine
|
||||
*/
|
||||
void setup_wifi()
|
||||
{
|
||||
// Setup WiFi - Use the ESP as a standalone access point
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP(network_ssid, network_password);
|
||||
Serial.println("WiFi SSID: ");
|
||||
Serial.print(network_ssid);
|
||||
Serial.print("WiFi password: ");
|
||||
Serial.println(network_password);
|
||||
Serial.print("AP IP Address: ");
|
||||
Serial.println(WiFi.softAPIP());
|
||||
}
|
||||
|
||||
/**
|
||||
* Define an array of Routes for the WebKernel
|
||||
* Each route contains:
|
||||
* * one or more methods (ex: (GET | POST) handles both GET and POST requests),
|
||||
* * a URL pattern (* matches anything until a slash '/', # matches until the end)
|
||||
* * the name of a function accepting arguments: HttpRequest&, HttpResponse&
|
||||
*/
|
||||
Route routes[] = {
|
||||
{GET, "/", index},
|
||||
{GET, "/led", show_led},
|
||||
{GET, "/led/toggle", toggle_led}
|
||||
};
|
||||
|
||||
WebKernel webKernel(80, routes, sizeof(routes)/sizeof(routes[0])); // Global WebKernel
|
||||
|
||||
void setup() {
|
||||
|
||||
// Initialize Pins
|
||||
pinMode(ledPin, OUTPUT);
|
||||
digitalWrite(ledPin, HIGH);
|
||||
|
||||
// Start Serial
|
||||
Serial.begin(115200);
|
||||
delay(10);
|
||||
|
||||
// Setup wifi
|
||||
setup_wifi();
|
||||
|
||||
// Initialize webKernel
|
||||
webKernel.begin();
|
||||
webKernel.setInitHandler(init_webkernel); // important! tells the webkernel how to initialize each connection
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
webKernel.handleClients();
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Site Index</title>
|
||||
<link rel="spreadsheet" href="styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>Site Index</h1>
|
||||
<div id="led-state">loading</div>
|
||||
<input id="toggle-btn" type="button" value="toggle" />
|
||||
<script src="scripts.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
on
|
|
@ -0,0 +1,28 @@
|
|||
const stateUrl = '/led';
|
||||
const toggleUrl = '/led/toggle';
|
||||
|
||||
var stateElem = document.getElementById("led-state");
|
||||
var toggleBtn = document.getElementById("toggle-btn");
|
||||
|
||||
function loadState() {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', stateUrl, true);
|
||||
xhr.onreadystatechange = stateLoaded;
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', toggleUrl, true);
|
||||
xhr.onreadystatechange = stateLoaded;
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
function stateLoaded(e) {
|
||||
var state = e.target.response;
|
||||
stateElem.innerHTML = state;
|
||||
}
|
||||
|
||||
toggleBtn.addEventListener('click',toggle);
|
||||
|
||||
loadState();
|
|
@ -0,0 +1,138 @@
|
|||
/**
|
||||
* WebKernel Application using the SPIFFS filesystem
|
||||
*/
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <WebKernel.h>
|
||||
#include <FS.h>
|
||||
|
||||
const char* network_ssid = "ESPThing"; // Name of the WiFi Access Point to create
|
||||
const char* network_password = ""; // Password for WiFi (empty is no password)
|
||||
|
||||
const uint8_t ledPin = LED_BUILTIN; // Arduino Pin controlling the LED
|
||||
|
||||
uint8_t content_buffer[64] = {}; // global buffer to contain response data
|
||||
Buffer content(content_buffer, // print response data to this variable
|
||||
sizeof(content_buffer)/sizeof(content_buffer[0]));
|
||||
File contentFile; // global file pointer to contain a response file
|
||||
|
||||
|
||||
/**
|
||||
* Handle requests for the site index (URL: /)
|
||||
*/
|
||||
void index(HttpRequest& request, HttpResponse& response)
|
||||
{
|
||||
request.setUrl("/index.html");
|
||||
serve_static(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle requests to show the LED status
|
||||
* This function should print the status of the LED pin to content
|
||||
*/
|
||||
void show_led(HttpRequest& request, HttpResponse& response)
|
||||
{
|
||||
response.headers.set("Content-Type","text/plain");
|
||||
if (digitalRead(ledPin))
|
||||
content.println("off");
|
||||
else content.println("on");
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle requests to toggle the LED state
|
||||
* Afterwards, send the user the LED's status
|
||||
*/
|
||||
void toggle_led(HttpRequest& request, HttpResponse& response)
|
||||
{
|
||||
digitalWrite(ledPin, !(digitalRead(ledPin)));
|
||||
show_led(request, response);
|
||||
}
|
||||
|
||||
void serve_static(HttpRequest& request, HttpResponse& response)
|
||||
{
|
||||
Serial.println("Serving static content:");
|
||||
Serial.println(request.getUrl());
|
||||
// TODO: handle query string (after ?)
|
||||
contentFile = SPIFFS.open(request.getUrl(),"r");
|
||||
if (!contentFile) {
|
||||
handle_not_found(request, response);
|
||||
return;
|
||||
}
|
||||
response.content = &contentFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Before any request is handled, clear the content buffer from the last
|
||||
* request and setup the buffer for the new request.
|
||||
*/
|
||||
void init_webkernel(HttpRequest& request, HttpResponse& response)
|
||||
{
|
||||
if (contentFile) contentFile.close();
|
||||
content.clear(); // clear previously printed content
|
||||
response.content = &content; // attach content buffer to the response
|
||||
}
|
||||
|
||||
void handle_not_found(HttpRequest& request, HttpResponse& response)
|
||||
{
|
||||
content.println("File Not Found");
|
||||
response.code = 404;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup a WiFi connection
|
||||
* Called during setup routine
|
||||
*/
|
||||
void setup_wifi()
|
||||
{
|
||||
// Setup WiFi - Use the ESP as a standalone access point
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP(network_ssid, network_password);
|
||||
Serial.println("WiFi SSID: ");
|
||||
Serial.print(network_ssid);
|
||||
Serial.print("WiFi password: ");
|
||||
Serial.println(network_password);
|
||||
Serial.print("AP IP Address: ");
|
||||
Serial.println(WiFi.softAPIP());
|
||||
}
|
||||
|
||||
/**
|
||||
* Define an array of Routes for the WebKernel
|
||||
* Each route contains:
|
||||
* * one or more methods (ex: (GET | POST) handles both GET and POST requests),
|
||||
* * a URL pattern (* matches anything until a slash '/', # matches until the end)
|
||||
* * the name of a function accepting arguments: HttpRequest&, HttpResponse&
|
||||
*/
|
||||
Route routes[] = {
|
||||
{GET, "/", index},
|
||||
{GET, "/led", show_led},
|
||||
{GET, "/led/toggle", toggle_led},
|
||||
{GET, "/#", serve_static}
|
||||
};
|
||||
|
||||
WebKernel webKernel(80, routes, sizeof(routes)/sizeof(routes[0])); // Global WebKernel
|
||||
|
||||
void setup() {
|
||||
|
||||
// Initialize Pins
|
||||
pinMode(ledPin, OUTPUT);
|
||||
digitalWrite(ledPin, HIGH);
|
||||
|
||||
// Start Serial
|
||||
Serial.begin(115200);
|
||||
delay(10);
|
||||
|
||||
// Setup wifi
|
||||
setup_wifi();
|
||||
|
||||
// Initialize webKernel
|
||||
webKernel.begin();
|
||||
webKernel.setInitHandler(init_webkernel); // important! tells the webkernel how to initialize each connection
|
||||
|
||||
// Initialize Filesystem
|
||||
SPIFFS.begin();
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
webKernel.handleClients();
|
||||
}
|
||||
|
|
@ -10,44 +10,12 @@ HttpResponse::HttpResponse(Stream& buffer):
|
|||
httpver()
|
||||
{};
|
||||
|
||||
HttpResponse::HttpResponse(Stream& buffer, unsigned int code):
|
||||
content(&buffer),
|
||||
code(code),
|
||||
reason(),
|
||||
httpver()
|
||||
{};
|
||||
|
||||
HttpResponse::HttpResponse(Stream& buffer, unsigned int code, const char * reason):
|
||||
content(&buffer),
|
||||
code(code),
|
||||
reason(),
|
||||
httpver()
|
||||
{
|
||||
setReason(reason);
|
||||
}
|
||||
|
||||
HttpResponse::HttpResponse():
|
||||
content(),
|
||||
code(200),
|
||||
reason(),
|
||||
httpver()
|
||||
{};
|
||||
|
||||
HttpResponse::HttpResponse(unsigned int code):
|
||||
content(),
|
||||
code(code),
|
||||
reason(),
|
||||
httpver()
|
||||
{}
|
||||
|
||||
HttpResponse::HttpResponse(unsigned int code, const char * reason):
|
||||
content(),
|
||||
code(code),
|
||||
reason(),
|
||||
httpver()
|
||||
{
|
||||
setReason(reason);
|
||||
}
|
||||
|
||||
const char * HttpResponse::setReason(const char * reason)
|
||||
{
|
||||
|
|
|
@ -16,11 +16,7 @@ class HttpResponse
|
|||
{
|
||||
public:
|
||||
HttpResponse(Stream&); // TODO: only cnstr needed by WebKernel, add httpver param
|
||||
HttpResponse(Stream&, unsigned int); // TODO: remove unnecessary constructors
|
||||
HttpResponse(Stream&, unsigned int, const char *);
|
||||
HttpResponse();
|
||||
HttpResponse(unsigned int code);
|
||||
HttpResponse(unsigned int code, const char *);
|
||||
~HttpResponse();
|
||||
unsigned int code;
|
||||
HttpHeaders headers;
|
||||
|
|
|
@ -40,7 +40,11 @@ void WebKernel::handleClients()
|
|||
keepClient = false;
|
||||
break;
|
||||
}
|
||||
dispatchRequest();
|
||||
#ifdef DEBUG
|
||||
Serial.println("Dispatching");
|
||||
#endif
|
||||
HttpResponse response;
|
||||
dispatchRequest(response);
|
||||
keepClient = false;
|
||||
break;
|
||||
}
|
||||
|
@ -61,12 +65,22 @@ void WebKernel::handleClients()
|
|||
|
||||
}
|
||||
|
||||
void WebKernel::dispatchRequest()
|
||||
void WebKernel::dispatchRequest(HttpResponse& response)
|
||||
{
|
||||
HttpResponse response;
|
||||
response.setHttpVersion(_request.getHttpVersion());
|
||||
if (_initHandler != nullptr)
|
||||
#ifdef DEBUG
|
||||
Serial.print("Is init handler set? ");
|
||||
#endif
|
||||
if (_initHandler != nullptr) {
|
||||
#ifdef DEBUG
|
||||
Serial.println("Yes");
|
||||
#endif
|
||||
_initHandler(_request, response);
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
Serial.println("No");
|
||||
#endif
|
||||
}
|
||||
_dispatcher.handle(_request, response);
|
||||
response.send(_client);
|
||||
if (_terminateHandler != nullptr)
|
||||
|
|
|
@ -69,7 +69,7 @@ class WebKernel
|
|||
WebKernelState _state;
|
||||
unsigned long int _stateChange;
|
||||
|
||||
void dispatchRequest();
|
||||
void dispatchRequest(HttpResponse& response);
|
||||
|
||||
void (*_initHandler)(HttpRequest&, HttpResponse&);
|
||||
void (*_terminateHandler)(const HttpRequest&, const HttpResponse&);
|
||||
|
|
Loading…
Reference in New Issue