From db0cd9769beabf5e7ab519b58a1fc478f193cb00 Mon Sep 17 00:00:00 2001 From: Kenneth Barbour Date: Wed, 25 Apr 2018 12:37:37 -0400 Subject: [PATCH] Usage documentation --- README.markdown | 126 +++++++++++++++++++++++++++++++++++++ README.md | 7 --- src/HttpResponse.h | 1 + test/test_HttpResponse.cpp | 9 +++ 4 files changed, 136 insertions(+), 7 deletions(-) create mode 100644 README.markdown delete mode 100644 README.md diff --git a/README.markdown b/README.markdown new file mode 100644 index 0000000..39ee40e --- /dev/null +++ b/README.markdown @@ -0,0 +1,126 @@ +HttpServer +========== +A Complicated Arduino Web Server Implementation + +## Features +* Designed around the popular ESP8266 playform +* Pattern-based request routing allows a callback to handle URL requests matching simple patterns +* Basic Buffer class provided for Response content, as well as support for File and other Stream-based content +* Simple request lifecycle control allows processing before and after requests are made +* Error handling callback functions + +## Usage + +Included here, a guide for implementing a Web Application as part of your Arduino project. For simplicity, +this guide stores variables and implements function globally. + +### Define +Include WebKernel.h and define a _Routes_ array, _WebKernel_ object, and a _Response Buffer_ to store +response data. The _WebKernel_ contructor needs the _port_ number, _routes_ array, and the _number_ of routes in the array. + +``` +/* + * File: example.ino + */ +#include + +// Define maximum size of content buffer +#define CONTENT_SIZE 512 + +// TODO: put function prototypes here + +Routes routes = { + { GET, "/", handle_index }, + { GET|POST, "/foo", handle_foo }, + { POST, "/bar", handle_post_bar }, + { GET, "/bar", handle_get_bar } + }; + +WebKernel webKernel(80, routes, sizeof(routes)/sizeof(routes[0])); + +uint8_t content_data[CONTENT_SIZE]; // Global block of memory to store content +Buffer content(content_data, CONTENT_SIZE); // Buffer to write data to previously allocated content_data + +``` + +### Implement +Each route defined in the routes array will need a function implemented to handle requests for that route. +You can put them in the main sketch .ino file anywhere as long as you include a function prototype before the Routes definition. +Alternatively, you could write prototypes in a file like routes.h, and implement them in a file like routes.cpp. + +``` +// Function prototypes +void handle_index (HttpRequest&, HttpResponse&); // handle GET request for '/' +void handle_foo (HttpRequest&, HttpResponse&); // handle GET or POST request for '/foo' +void handle_post_bar (HttpRequest&, HttpResponse&); // handle POST request for '/bar' +void handle_get_bar (HttpRequest&, HttpResponse&); // handle GET request for '/bar' +``` + +Function prototypes should be put before any other reference to one of the functions is made. In this example, they should be defined before defining the _Route_ array. + +Next, implement each of the functions we just defined: + +``` +void handle_index(HttpRequest& request, HttpResponse& response) +{ + response.setContent(content); + content.println("Site Index

Site Index

"); +} +``` + +### Initialize +Initialize the webKernel in your sketch's setup function. + +``` +void setup() +{ + // initialize Serial, WiFi, Ethernet, etc + + webKernel.begin(); +} +``` + +### Drive +The WebKernel has everything it needs to handle requests now. The only thing left is to make a call to webKernel.handleClients() as part of your sketch's loop function. + +``` +void loop() +{ + + webKernel.handleClients(); + + // ... include other sketch routines +} +``` + +### Additional Callbacks +For more complicated projects, such as projects also serving static files or other Stream objects, its helpful to have more control over the WebKernel throughout the request handling process. It is also helpful to have control over _Method Not Allowed_ and _File Not Found_ errors. + +Each of the callbacks listed below are of the form `void handler (HttpRequest&, HttpResponse&)` with the exception of the Terminate Handler, which is `void terminate_handler (const HttpRequest&, const HttpResponse&)`. + +``` +void setup() +{ + // ... other setup routines + + webKernel.begin(); + webKernel.setInitHandler(init_webkernel); // called before dispatching to the routes handler + webKernel.setTerminateHandler(term_webkernel); // called after sending response to client + webKernel.setNotFoundHandler(handle_not_found); // called if a route is not found for the request + webKernel.setMethodNotAllowedHandler(handle_method_not_allowed); // called if a route is found, but the request method is not allowed + +} +``` + +## Examples + +### Basic Web Application +Open the basic example in your Arduino IDE and upload it to your ESP8266. These examples use the ESP8266 as a Standalone Access Point, so refer to the sketch for the SSID and password, or connect it to a Serial monitor before it starts. + +To test, connect to its wireless access point and go to (http://192.168.1.4/), the default IP address for the ESP. + +### Filesystem Web Application +Similar to the basic example, except this example also includes static content in the _data_ directory that needs to be uploaded separately. + +To upload the static content to an ESP8266, you will need to use the ESP8266FS tool described here: (https://esp8266.github.io/Arduino/versions/2.0.0/doc/filesystem.html#uploading-files-to-file-system) + diff --git a/README.md b/README.md deleted file mode 100644 index 4a42060..0000000 --- a/README.md +++ /dev/null @@ -1,7 +0,0 @@ -#TODO: -* header name should be case-insensitive -* HttpRequest.capture() handle improperly formed request -* HttpRequest.capture() handle running out of stream -* Throw exceptions for unknown http methods -* Router throws exceptions for not found or method not allowed -* RequestDispatcher that calls router and catches exceptions diff --git a/src/HttpResponse.h b/src/HttpResponse.h index 079e44e..aa67a46 100644 --- a/src/HttpResponse.h +++ b/src/HttpResponse.h @@ -30,6 +30,7 @@ class HttpResponse const char * getReason() const; static const char * getDefaultReason(unsigned int); Stream * content; + void setContent(Stream& stream) { content = &stream; } size_t send(Print&); diff --git a/test/test_HttpResponse.cpp b/test/test_HttpResponse.cpp index 19e4e4a..4408749 100644 --- a/test/test_HttpResponse.cpp +++ b/test/test_HttpResponse.cpp @@ -68,3 +68,12 @@ TEST_CASE("HttpResponse get/setHttpVersion", "[HttpResponse]") r.setHttpVersion("AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPP"); } } + +TEST_CASE("HttpResponse setContent", "[HttpResponse]") +{ + HttpResponse r; + uint8_t buff[10]; + Buffer content(buff, sizeof(buff)); + + r.setContent(content); +}