From cfda6cbd06ef92688ea92c196c3d2d98b632ce22 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 27 Mar 2015 11:17:45 +0300 Subject: [PATCH] Add ESP8266WebServer library --- cores/esp8266/abi.cpp | 6 + .../examples/HelloServer/HelloServer.ino | 54 +++ libraries/ESP8266WebServer/keywords.txt | 31 ++ .../ESP8266WebServer/src/ESP8266WebServer.cpp | 326 ++++++++++++++++++ .../ESP8266WebServer/src/ESP8266WebServer.h | 83 +++++ 5 files changed, 500 insertions(+) create mode 100644 libraries/ESP8266WebServer/examples/HelloServer/HelloServer.ino create mode 100644 libraries/ESP8266WebServer/keywords.txt create mode 100644 libraries/ESP8266WebServer/src/ESP8266WebServer.cpp create mode 100644 libraries/ESP8266WebServer/src/ESP8266WebServer.h diff --git a/cores/esp8266/abi.cpp b/cores/esp8266/abi.cpp index 72e0cc76a..7eadfad10 100644 --- a/cores/esp8266/abi.cpp +++ b/cores/esp8266/abi.cpp @@ -52,4 +52,10 @@ void __cxa_deleted_virtual(void) { abort(); } +namespace std { + void __throw_bad_function_call() { + abort(); + } +} + diff --git a/libraries/ESP8266WebServer/examples/HelloServer/HelloServer.ino b/libraries/ESP8266WebServer/examples/HelloServer/HelloServer.ino new file mode 100644 index 000000000..5cbbf9b4b --- /dev/null +++ b/libraries/ESP8266WebServer/examples/HelloServer/HelloServer.ino @@ -0,0 +1,54 @@ +#include +#include +#include + +const char* ssid = "..............."; +const char* password = "..............."; + + +ESP8266WebServer server(80); + +const int led = 13; + +void handle_root() { + digitalWrite(led, 1); + server.send(200, "text/plain", form); + delay(100); + digitalWrite(led, 0); +} + +void setup(void) +{ + Serial.begin(115200); + pinMode(led, OUTPUT); + digitalWrite(led, 0); + + // Connect to WiFi network + WiFi.begin(ssid, password); + Serial.println(""); + + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(""); + Serial.print("Connected to "); + Serial.println(ssid); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + + server.on("/", handle_root); + + server.on("/inline", [](){ + server.send(200, "text/plain", "this works as well"); + }); + + server.begin(); + Serial.println("HTTP server started"); +} + +void loop(void) +{ + server.handleClient(); +} diff --git a/libraries/ESP8266WebServer/keywords.txt b/libraries/ESP8266WebServer/keywords.txt new file mode 100644 index 000000000..eba2b2144 --- /dev/null +++ b/libraries/ESP8266WebServer/keywords.txt @@ -0,0 +1,31 @@ +####################################### +# Syntax Coloring Map For Ultrasound +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +ESP8266WebServer KEYWORD1 +HTTPMethod KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +handleClient KEYWORD2 +on KEYWORD2 +uri KEYWORD2 +method KEYWORD2 +client KEYWORD2 +send KEYWORD2 +arg KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + +HTTP_GET LITERAL1 +HTTP_POST LITERAL1 +HTTP_ANY LITERAL1 diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp new file mode 100644 index 000000000..0906dbc4e --- /dev/null +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp @@ -0,0 +1,326 @@ +/* + ESP8266WebServer.cpp - Dead simple web-server. + Supports only one simultaneous client, knows how to handle GET and POST. + + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +#include +#include "WiFiServer.h" +#include "WiFiClient.h" +#include "ESP8266WebServer.h" + +// #define DEBUG + +struct ESP8266WebServer::RequestHandler { + RequestHandler(ESP8266WebServer::THandlerFunction fn, const char* uri, HTTPMethod method) + : fn(fn) + , uri(uri) + , method(method) + , next(NULL) + { + } + + ESP8266WebServer::THandlerFunction fn; + String uri; + HTTPMethod method; + RequestHandler* next; + +}; + +ESP8266WebServer::ESP8266WebServer(int port) +: _server(port) +, _firstHandler(0) +, _lastHandler(0) +, _currentArgCount(0) +, _currentArgs(0) +{ +} + +ESP8266WebServer::~ESP8266WebServer() +{ + if (!_firstHandler) + return; + RequestHandler* handler = _firstHandler; + while (handler) { + RequestHandler* next = handler->next; + delete handler; + handler = next; + } +} + +void ESP8266WebServer::begin() { + _server.begin(); +} + + +void ESP8266WebServer::on(const char* uri, ESP8266WebServer::THandlerFunction handler) +{ + on(uri, HTTP_ANY, handler); +} + +void ESP8266WebServer::on(const char* uri, HTTPMethod method, ESP8266WebServer::THandlerFunction fn) +{ + RequestHandler* handler = new RequestHandler(fn, uri, method); + if (!_lastHandler) { + _firstHandler = handler; + _lastHandler = handler; + } + else { + _lastHandler->next = handler; + _lastHandler = handler; + } +} + +void ESP8266WebServer::handleClient() +{ + WiFiClient client = _server.available(); + if (!client) { + return; + } + +#ifdef DEBUG + Serial.println("New client"); +#endif + // Wait for data from client to become available + while(client.connected() && !client.available()){ + delay(1); + } + + // Read the first line of HTTP request + String req = client.readStringUntil('\r'); + client.readStringUntil('\n'); + + HTTPMethod method = HTTP_GET; + if (req.startsWith("POST")) { + method = HTTP_POST; + } + + + // First line of HTTP request looks like "GET /path HTTP/1.1" + // Retrieve the "/path" part by finding the spaces + int addr_start = req.indexOf(' '); + int addr_end = req.indexOf(' ', addr_start + 1); + if (addr_start == -1 || addr_end == -1) { +#ifdef DEBUG + Serial.print("Invalid request: "); + Serial.println(req); +#endif + return; + } + + req = req.substring(addr_start + 1, addr_end); + + String formData; + if (method == HTTP_POST) + { + int contentLength = -1; + int headerCount = 0; + while(headerCount < 1024) { // there shouldn't be that much really + String line = client.readStringUntil('\r'); + client.readStringUntil('\n'); + + if (line.length() > 0) { // this is a header + ++headerCount; + if (contentLength < 0 && line.startsWith("Content-Length")) { + // get content length from the header + int valuePos = line.indexOf(' ', 14); + if (valuePos > 0) { + String valueStr = line.substring(valuePos+1); + contentLength = valueStr.toInt(); +#ifdef DEBUG + Serial.print("Content-Length: "); + Serial.println(contentLength); +#endif + } + } + } + else { + break; + } + } +#ifdef DEBUG + Serial.print("headerCount="); + Serial.println(headerCount); +#endif + if (contentLength >= 0) { + formData = ""; + int n = 0; // timeout counter + while (formData.length() < contentLength && ++n < 3) + formData += client.readString(); + } + else { + formData = client.readStringUntil('\r'); // will return after timing out once + } + // read form data + // formData = + } + else if (method == HTTP_GET) + { + int args_start = req.indexOf('?'); + if (args_start != -1) + { + formData = req.substring(args_start + 1); + req = req.substring(0, args_start); + } + } + + client.flush(); + +#ifdef DEBUG + Serial.print("Request: "); + Serial.println(req); + Serial.print("Args: "); + Serial.println(formData); +#endif + + _parseArguments(formData); + _handleRequest(client, req, method); + +} + +void ESP8266WebServer::send(int code, const char* content_type, String content) { + String response = "HTTP/1.1 "; + response += String(code); + response += " "; + if (code == 200) + response += "OK"; + else if (code == 404) + response += "Not found"; + response += "\r\n"; + if (!content_type) + content_type = "text/html"; + response += "Content-Type: "; + response += content_type; + response += "\r\n\r\n"; + response += content; + _currentClient.print(response); +} + +String ESP8266WebServer::arg(const char* name) +{ + for (int i = 0; i < _currentArgCount; ++i) { + if (_currentArgs[i].key == name) + return _currentArgs[i].value; + } + return String(); +} + + +void ESP8266WebServer::_parseArguments(String data) +{ + if (_currentArgs) + delete[] _currentArgs; + _currentArgs = 0; + if (data.length() == 0) { + _currentArgCount = 0; + return; + } + _currentArgCount = 1; + + for (int i = 0; i < data.length(); ) { + i = data.indexOf('&', i); + if (i == -1) + break; + ++i; + ++_currentArgCount; + } +#ifdef DEBUG + Serial.print("args count: "); + Serial.println(_currentArgCount); +#endif + + _currentArgs = new RequestArgument[_currentArgCount]; + int pos = 0; + int iarg; + for (iarg = 0; iarg < _currentArgCount;) { + int equal_sign_index = data.indexOf('=', pos); + int next_arg_index = data.indexOf('&', pos); +#ifdef DEBUG + Serial.print("pos "); + Serial.print(pos); + Serial.print("=@ "); + Serial.print(equal_sign_index); + Serial.print(" &@ "); + Serial.println(next_arg_index); +#endif + if (equal_sign_index == -1 || equal_sign_index > next_arg_index && next_arg_index != -1) { +#ifdef DEBUG + Serial.print("arg missing value: "); + Serial.println(iarg); +#endif + if (next_arg_index == -1) + break; + pos = next_arg_index + 1; + continue; + } + RequestArgument& arg = _currentArgs[iarg]; + arg.key = data.substring(pos, equal_sign_index); + arg.value = data.substring(equal_sign_index + 1, next_arg_index); +#ifdef DEBUG + Serial.print("arg "); + Serial.print(iarg); + Serial.print(" key: "); + Serial.print(arg.key); + Serial.print(" value: "); + Serial.println(arg.value); +#endif + ++iarg; + if (next_arg_index == -1) + break; + pos = next_arg_index + 1; + } + _currentArgCount = iarg; +#ifdef DEBUG + Serial.print("args count: "); + Serial.println(_currentArgCount); +#endif + +} + + +void ESP8266WebServer::_handleRequest(WiFiClient& client, String uri, HTTPMethod method) { + _currentClient = client; + _currentUri = uri; + _currentMethod = method; + + RequestHandler* handler; + for (handler = _firstHandler; handler; handler = handler->next) + { + if (handler->method != HTTP_ANY && handler->method != method) + continue; + + if (handler->uri != uri) + continue; + + handler->fn(); + break; + } + + if (!handler) + { +#ifdef DEBUG + Serial.println("request handler not found"); +#endif + send(404, "text/plain", String("Not found: ") + uri); + } + + _currentClient = WiFiClient(); + _currentUri = String(); + +} diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h new file mode 100644 index 000000000..6d9c5ffb5 --- /dev/null +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h @@ -0,0 +1,83 @@ +/* + ESP8266WebServer.h - Dead simple web-server. + Supports only one simultaneous client, knows how to handle GET and POST. + + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +#ifndef ESP8266WEBSERVER_H +#define ESP8266WEBSERVER_H + +#include + +enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_POST }; + + +class ESP8266WebServer +{ +public: + + ESP8266WebServer(int port = 80); + ~ESP8266WebServer(); + + void begin(); + void handleClient(); + + typedef std::function THandlerFunction; + void on(const char* uri, THandlerFunction handler); + void on(const char* uri, HTTPMethod method, THandlerFunction fn); + + String uri() { return _currentUri; } + HTTPMethod method() { return _currentMethod; } + WiFiClient client() { return _currentClient; } + + // send response to the client + // code - HTTP response code, can be 200 or 404 + // content_type - HTTP content type, like "text/plain" or "image/png" + // content - actual content body + void send(int code, const char* content_type = NULL, String content = String("")); + + // get request argument value + String arg(const char* name); + +protected: + void _handleRequest(WiFiClient& client, String uri, HTTPMethod method); + void _parseArguments(String data); + + struct RequestHandler; + struct RequestArgument { + String key; + String value; + }; + + WiFiServer _server; + + WiFiClient _currentClient; + HTTPMethod _currentMethod; + String _currentUri; + + size_t _currentArgCount; + RequestArgument* _currentArgs; + + RequestHandler* _firstHandler; + RequestHandler* _lastHandler; + +}; + + +#endif //ESP8266WEBSERVER_H