feature/UrlUtils
Kenneth Barbour 2018-04-29 10:22:21 -04:00
commit 4db11a9318
11 changed files with 326 additions and 43 deletions

View File

@ -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

View File

@ -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();
}

View File

@ -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>

View File

@ -0,0 +1 @@
on

View File

@ -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();

View File

@ -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();
}

View File

@ -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(),
HttpResponse::HttpResponse():
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)
{

View File

@ -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;

View File

@ -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)

View File

@ -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&);