From b7321a23b12d2e50253b2829d241d0e7506afb1b Mon Sep 17 00:00:00 2001 From: Dave Falkenburg Date: Mon, 26 Sep 2011 17:30:54 -0700 Subject: [PATCH 01/75] Arduino 1.0 changes --- WebServer.cpp | 16 ++++++++-------- WebServer.h | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/WebServer.cpp b/WebServer.cpp index 5d9a88d..a781d8b 100644 --- a/WebServer.cpp +++ b/WebServer.cpp @@ -60,24 +60,24 @@ void WebServer::addCommand(const char *verb, Command *cmd) } } -void WebServer::write(uint8_t ch) +size_t WebServer::write(uint8_t ch) { - m_client.write(ch); + return m_client.write(ch); } -void WebServer::write(const char *str) +size_t WebServer::write(const char *str) { - m_client.write(str); + return m_client.write(str); } -void WebServer::write(const uint8_t *buffer, size_t size) +size_t WebServer::write(const uint8_t *buffer, size_t size) { - m_client.write(buffer, size); + return m_client.write(buffer, size); } -void WebServer::write(const char *buffer, size_t length) +size_t WebServer::write(const char *buffer, size_t length) { - m_client.write((const uint8_t *)buffer, length); + return m_client.write((const uint8_t *)buffer, length); } void WebServer::writeP(const prog_uchar *data, size_t length) diff --git a/WebServer.h b/WebServer.h index bdcd614..4879f44 100644 --- a/WebServer.h +++ b/WebServer.h @@ -29,8 +29,8 @@ #include #include "avr/pgmspace.h" -#include -#include +#include +#include /******************************************************************** * CONFIGURATION @@ -200,14 +200,14 @@ class WebServer: public Print void httpSeeOther(const char *otherURL); // implementation of write used to implement Print interface - virtual void write(uint8_t); - virtual void write(const char *str); - virtual void write(const uint8_t *buffer, size_t size); - void write(const char *data, size_t length); + virtual size_t write(uint8_t); + virtual size_t write(const char *str); + virtual size_t write(const uint8_t *buffer, size_t size); + size_t write(const char *data, size_t length); private: - Server m_server; - Client m_client; + EthernetServer m_server; + EthernetClient m_client; const char *m_urlPrefix; char m_pushback[32]; From 90f5140b29d0af0d3bb76d0d3159b9855ca37596 Mon Sep 17 00:00:00 2001 From: Chris Lee Date: Thu, 8 Dec 2011 01:04:05 +0800 Subject: [PATCH 02/75] issue 2: send CRLF for extraHeaders httpSuccess() --- WebServer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/WebServer.cpp b/WebServer.cpp index 5d9a88d..afff8ce 100644 --- a/WebServer.cpp +++ b/WebServer.cpp @@ -263,8 +263,10 @@ void WebServer::httpSuccess(const char *contentType, printP(successMsg1); print(contentType); printCRLF(); - if (extraHeaders) + if (extraHeaders) { print(extraHeaders); + printCRLF(); + } printCRLF(); } From 8ceb13395974af9e6c95cd489533750d6e7d56c3 Mon Sep 17 00:00:00 2001 From: Martin Lormes Date: Fri, 30 Dec 2011 22:59:43 +0100 Subject: [PATCH 03/75] bumped to Webduino 1.5, handling favicon.ico requests, added keywords.txt for syntax highlighting --- keywords.txt | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 keywords.txt diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..c1365ab --- /dev/null +++ b/keywords.txt @@ -0,0 +1,23 @@ +WebServer KEYWORD1 +ConnectionType KEYWORD1 +begin KEYWORD2 +processConnection KEYWORD2 +setDefaultCommand KEYWORD2 +setFailureCommand KEYWORD2 +addCommand KEYWORD2 +printCRLF KEYWORD2 +printP KEYWORD2 +writeP KEYWORD2 +radioButton KEYWORD2 +checkBox KEYWORD2 +read KEYWORD2 +push KEYWORD2 +expect KEYWORD2 +readInt KEYWORD2 +readPOSTparam KEYWORD2 +nextURLparam KEYWORD2 +httpFail KEYWORD2 +httpSuccess KEYWORD2 +httpSeeOther KEYWORD2 +write KEYWORD2 +P KEYWORD2 From dbd2c9a1deba801045e203b65b8589bddbe822b0 Mon Sep 17 00:00:00 2001 From: Martin Lormes Date: Fri, 30 Dec 2011 23:03:08 +0100 Subject: [PATCH 04/75] bumped to Webduino 1.5, handling favicon.ico requests, added keywords.txt for syntax highlighting --- WebServer.cpp | 30 ++++++++++- WebServer.h | 7 +-- examples/Web_HelloWorld/Web_HelloWorld.pde | 4 +- readme.md | 62 +++++++++++++++++++--- 4 files changed, 91 insertions(+), 12 deletions(-) diff --git a/WebServer.cpp b/WebServer.cpp index c0c74e1..8a18904 100644 --- a/WebServer.cpp +++ b/WebServer.cpp @@ -206,6 +206,10 @@ void WebServer::processConnection(char *buff, int *bufflen) { noRobots(requestType); } + else if (strcmp(buff, "/favicon.ico") == 0) + { + favicon(requestType); + } else if (requestType == INVALID || strncmp(buff, m_urlPrefix, urlPrefixLen) != 0 || !dispatchCommand(requestType, buff + urlPrefixLen, @@ -251,6 +255,30 @@ void WebServer::noRobots(ConnectionType type) } } +void WebServer::favicon(ConnectionType type) +{ + httpSuccess("image/x-icon"); + if (type != HEAD) + { + P(faviconIco) = { + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x10, 0x10, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0xb0, 0x00, + 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xcf, 0xbf, 0x00, 0x00, 0xc7, 0xbf, 0x00, 0x00, 0xc3, 0xbf, 0x00, 0x00, 0xc1, 0xbf, + 0x00, 0x00, 0xc0, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xbf, 0x00, 0x00, 0xc1, 0xbf, + 0x00, 0x00, 0xc3, 0xbf, 0x00, 0x00, 0xc7, 0xbf, 0x00, 0x00, 0xcf, 0xbf, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + writeP(faviconIco, sizeof(faviconIco)); + } +} + void WebServer::httpSuccess(const char *contentType, const char *extraHeaders) { @@ -258,7 +286,7 @@ void WebServer::httpSuccess(const char *contentType, "HTTP/1.0 200 OK" CRLF WEBDUINO_SERVER_HEADER "Access-Control-Allow-Origin: *" CRLF - "Content-Type: "; + "Content-Type: "; printP(successMsg1); print(contentType); diff --git a/WebServer.h b/WebServer.h index 4879f44..161ae94 100644 --- a/WebServer.h +++ b/WebServer.h @@ -36,8 +36,8 @@ * CONFIGURATION ********************************************************************/ -#define WEBDUINO_VERSION 1004 -#define WEBDUINO_VERSION_STRING "1.4" +#define WEBDUINO_VERSION 1005 +#define WEBDUINO_VERSION_STRING "1.5" #if WEBDUINO_SUPRESS_SERVER_HEADER #define WEBDUINO_SERVER_HEADER "" @@ -112,7 +112,7 @@ class WebServer: public Print char *url_tail, bool tail_complete); // constructor for webserver object - WebServer(const char *urlPrefix = "/", int port = 80); + WebServer(const char *urlPrefix = "", int port = 80); // start listening for connections void begin(); @@ -237,6 +237,7 @@ class WebServer: public Print static void defaultFailCmd(WebServer &server, ConnectionType type, char *url_tail, bool tail_complete); void noRobots(ConnectionType type); + void favicon(ConnectionType type); }; #endif // WEBDUINO_H_ diff --git a/examples/Web_HelloWorld/Web_HelloWorld.pde b/examples/Web_HelloWorld/Web_HelloWorld.pde index d523f26..aebf90a 100644 --- a/examples/Web_HelloWorld/Web_HelloWorld.pde +++ b/examples/Web_HelloWorld/Web_HelloWorld.pde @@ -17,8 +17,8 @@ static uint8_t mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; static uint8_t ip[] = { 192, 168, 1, 210 }; /* This creates an instance of the webserver. By specifying a prefix - * of "/", all pages will be at the root of the server. */ -#define PREFIX "/" + * of "", all pages will be at the root of the server. */ +#define PREFIX "" WebServer webserver(PREFIX, 80); /* commands are functions that get called by the webserver framework diff --git a/readme.md b/readme.md index 934ff43..23a616c 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,6 @@ # Webduino -### Project forked from 1.4.1 of http://code.google.com/p/webduino/ By Ben Combee + +### Project forked from 1.4.1 of http://code.google.com/p/webduino/ by Ben Combee This is an Arduino-based Web Server library, originally developed for a class at NYC Resistor. It's called Webduino, and it's an extensible web server library for the Arduino using the Wiznet-based Ethernet shields. It's released under the MIT license allowing all sorts of reuse. @@ -7,26 +8,29 @@ I've got a few examples up right now -- the Buzz example interfaces with Zach's I hope to add a few more examples in the next few weeks, including a web-enabled fridge sign using a serial LCD and how to serve things other than just HTML text from the device. I also need to work on more documentation and finishing up my slides. -### Features +## Features + - URL parameter parsing - Handle HTTP Methods, e.g. GET, POST - Web Forms - Images - JSON/RESTful interface -### Installation Notes +## Installation Notes -With Arduino 0022, add the Webduino folder to the "libraries" folder of your sketchbook directory. +With Arduino 1.0, add the Webduino folder to the "libraries" folder of your sketchbook directory. You can put the examples in your own sketchbook directory, or in hardware/libraries/webduino/examples, as you prefer. If you get an error message when building the examples similar to "WebServer.h not found", it's a problem with where you put the Webduino folder. The server won't work if the header is directly in the libraries folder. -### Presentation +## Presentation + [Wedbuino Presentation on Google Docs](http://docs.google.com/present/view?id=dd8gqxt8_5c8w9qfg3) -### Compatible Ethernet Shields +## Compatible Ethernet Shields + These have all been tested with the library successfully: - Freetronics Etherten @@ -35,4 +39,50 @@ These have all been tested with the library successfully: - Adafruit Ethernet Shield w/ Wiznet 811MJ module - NKC Electronics Ethernet Shield DIY Kit +## Version history + +### 1.5 released in Dec 2011 + +- added a default favicon.ico based on the led.png from the Image example to save resources on Firefox trying to load this file on every request if it can't be loaded +- added keywords.txt for syntax highlighting in Arduino IDE +- bumped the version number up in response headers and compiler variables +- added version history to readme +- fixed default value for prefix +- fixed /index.html in Hello World example + +### releases between Jul 2011 and Dec 2011 + +- project forked on GitHub by Chris Lee +- JSON/RESTful interface +- header Access-Control-Allow-Origin added +- code split in .h and .cpp files +- Arduino 1.0 changes by Dave Falkenburg and others +- adding CRLF after extraHeaders + +### 1.4.1 released in Nov 2009 + +- fix examples to use readPOSTparam method + +### 1.4 released in Sep 2009 + +- bug fix for multple connections + +### 1.3.1 released in Aug 2009 + +- major bug fix for 1.3 for posts where Content-Length is last header sent + +### 1.3 released in Aug 2009 + +- mostly robustness fixes (beta) + +### 1.2.1 released in Aug 2009 + +- fixed HelloWorld example + +### 1.2 released in Jul 2009 + +- now with GET parameter handling + +### 1.1 released in Apr 2009 +### 1.0 released in Mar 2009 From 5daa62c32d1d77cdaa3fc07737223f54d8b7b82c Mon Sep 17 00:00:00 2001 From: Martin Lormes Date: Sun, 1 Jan 2012 22:55:22 +0100 Subject: [PATCH 05/75] fixed line break in serial debug output --- WebServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebServer.cpp b/WebServer.cpp index 8a18904..bd01bbf 100644 --- a/WebServer.cpp +++ b/WebServer.cpp @@ -192,7 +192,7 @@ void WebServer::processConnection(char *buff, int *bufflen) #if WEBDUINO_SERIAL_DEBUGGING > 1 Serial.print("*** requestType = "); Serial.print((int)requestType); - Serial.println(", request = \""); + Serial.print(", request = \""); Serial.print(buff); Serial.println("\" ***"); #endif From ab23635620da2aade3c3eb0b3c7f6680656c5db4 Mon Sep 17 00:00:00 2001 From: Martin Lormes Date: Sun, 1 Jan 2012 23:00:35 +0100 Subject: [PATCH 06/75] moving contents of WebServer.cpp and WebServer.h back together in one file: fixes issue with #define statements (serial debugging in particular) --- WebServer.cpp | 770 -------------------------------------------------- WebServer.h | 749 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 749 insertions(+), 770 deletions(-) delete mode 100644 WebServer.cpp diff --git a/WebServer.cpp b/WebServer.cpp deleted file mode 100644 index bd01bbf..0000000 --- a/WebServer.cpp +++ /dev/null @@ -1,770 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-file-style: "k&r"; c-basic-offset: 2; -*- - - Webduino, a simple Arduino web server - Copyright 2009 Ben Combee, Ran Talbott - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -#include - -WebServer::WebServer(const char *urlPrefix, int port) : - m_server(port), - m_client(255), - m_urlPrefix(urlPrefix), - m_pushbackDepth(0), - m_cmdCount(0), - m_contentLength(0), - m_failureCmd(&defaultFailCmd), - m_defaultCmd(&defaultFailCmd) -{ -} - -void WebServer::begin() -{ - m_server.begin(); -} - -void WebServer::setDefaultCommand(Command *cmd) -{ - m_defaultCmd = cmd; -} - -void WebServer::setFailureCommand(Command *cmd) -{ - m_failureCmd = cmd; -} - -void WebServer::addCommand(const char *verb, Command *cmd) -{ - if (m_cmdCount < SIZE(m_commands)) - { - m_commands[m_cmdCount].verb = verb; - m_commands[m_cmdCount++].cmd = cmd; - } -} - -size_t WebServer::write(uint8_t ch) -{ - return m_client.write(ch); -} - -size_t WebServer::write(const char *str) -{ - return m_client.write(str); -} - -size_t WebServer::write(const uint8_t *buffer, size_t size) -{ - return m_client.write(buffer, size); -} - -size_t WebServer::write(const char *buffer, size_t length) -{ - return m_client.write((const uint8_t *)buffer, length); -} - -void WebServer::writeP(const prog_uchar *data, size_t length) -{ - // copy data out of program memory into local storage, write out in - // chunks of 32 bytes to avoid extra short TCP/IP packets - uint8_t buffer[32]; - size_t bufferEnd = 0; - - while (length--) - { - if (bufferEnd == 32) - { - m_client.write(buffer, 32); - bufferEnd = 0; - } - - buffer[bufferEnd++] = pgm_read_byte(data++); - } - - if (bufferEnd > 0) - m_client.write(buffer, bufferEnd); -} - -void WebServer::printP(const prog_uchar *str) -{ - // copy data out of program memory into local storage, write out in - // chunks of 32 bytes to avoid extra short TCP/IP packets - uint8_t buffer[32]; - size_t bufferEnd = 0; - - while (buffer[bufferEnd++] = pgm_read_byte(str++)) - { - if (bufferEnd == 32) - { - m_client.write(buffer, 32); - bufferEnd = 0; - } - } - - // write out everything left but trailing NUL - if (bufferEnd > 1) - m_client.write(buffer, bufferEnd - 1); -} - -void WebServer::printCRLF() -{ - m_client.write((const uint8_t *)"\r\n", 2); -} - -bool WebServer::dispatchCommand(ConnectionType requestType, char *verb, - bool tail_complete) -{ - if ((verb[0] == 0) || ((verb[0] == '/') && (verb[1] == 0))) - { - m_defaultCmd(*this, requestType, verb, tail_complete); - return true; - } - // We now know that the URL contains at least one character. And, - // if the first character is a slash, there's more after it. - if (verb[0] == '/') - { - char i; - char *qm_loc; - int verb_len; - int qm_offset; - // Skip over the leading "/", because it makes the code more - // efficient and easier to understand. - verb++; - // Look for a "?" separating the filename part of the URL from the - // parameters. If it's not there, compare to the whole URL. - qm_loc = strchr(verb, '?'); - verb_len = (qm_loc == NULL) ? strlen(verb) : (qm_loc - verb); - qm_offset = (qm_loc == NULL) ? 0 : 1; - for (i = 0; i < m_cmdCount; ++i) - { - if ((verb_len == strlen(m_commands[i].verb)) - && (strncmp(verb, m_commands[i].verb, verb_len) == 0)) - { - // Skip over the "verb" part of the URL (and the question - // mark, if present) when passing it to the "action" routine - m_commands[i].cmd(*this, requestType, - verb + verb_len + qm_offset, - tail_complete); - return true; - } - } - } - return false; -} - -// processConnection with a default buffer -void WebServer::processConnection() -{ - char request[WEBDUINO_DEFAULT_REQUEST_LENGTH]; - int request_len = WEBDUINO_DEFAULT_REQUEST_LENGTH; - processConnection(request, &request_len); -} - -void WebServer::processConnection(char *buff, int *bufflen) -{ - m_client = m_server.available(); - - if (m_client) { - m_readingContent = false; - buff[0] = 0; - ConnectionType requestType = INVALID; -#if WEBDUINO_SERIAL_DEBUGGING > 1 - Serial.println("*** checking request ***"); -#endif - getRequest(requestType, buff, bufflen); -#if WEBDUINO_SERIAL_DEBUGGING > 1 - Serial.print("*** requestType = "); - Serial.print((int)requestType); - Serial.print(", request = \""); - Serial.print(buff); - Serial.println("\" ***"); -#endif - processHeaders(); -#if WEBDUINO_SERIAL_DEBUGGING > 1 - Serial.println("*** headers complete ***"); -#endif - - int urlPrefixLen = strlen(m_urlPrefix); - if (strcmp(buff, "/robots.txt") == 0) - { - noRobots(requestType); - } - else if (strcmp(buff, "/favicon.ico") == 0) - { - favicon(requestType); - } - else if (requestType == INVALID || - strncmp(buff, m_urlPrefix, urlPrefixLen) != 0 || - !dispatchCommand(requestType, buff + urlPrefixLen, - (*bufflen) >= 0)) - { - m_failureCmd(*this, requestType, buff, (*bufflen) >= 0); - } - -#if WEBDUINO_SERIAL_DEBUGGING > 1 - Serial.println("*** stopping connection ***"); -#endif - m_client.stop(); - } -} - -void WebServer::httpFail() -{ - P(failMsg) = - "HTTP/1.0 400 Bad Request" CRLF - WEBDUINO_SERVER_HEADER - "Content-Type: text/html" CRLF - CRLF - WEBDUINO_FAIL_MESSAGE; - - printP(failMsg); -} - -void WebServer::defaultFailCmd(WebServer &server, - WebServer::ConnectionType type, - char *url_tail, - bool tail_complete) -{ - server.httpFail(); -} - -void WebServer::noRobots(ConnectionType type) -{ - httpSuccess("text/plain"); - if (type != HEAD) - { - P(allowNoneMsg) = "User-agent: *" CRLF "Disallow: /" CRLF; - printP(allowNoneMsg); - } -} - -void WebServer::favicon(ConnectionType type) -{ - httpSuccess("image/x-icon"); - if (type != HEAD) - { - P(faviconIco) = { - 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x10, 0x10, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0xb0, 0x00, - 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, - 0x00, 0x00, 0xcf, 0xbf, 0x00, 0x00, 0xc7, 0xbf, 0x00, 0x00, 0xc3, 0xbf, 0x00, 0x00, 0xc1, 0xbf, - 0x00, 0x00, 0xc0, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xbf, 0x00, 0x00, 0xc1, 0xbf, - 0x00, 0x00, 0xc3, 0xbf, 0x00, 0x00, 0xc7, 0xbf, 0x00, 0x00, 0xcf, 0xbf, 0x00, 0x00, 0xff, 0xff, - 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - writeP(faviconIco, sizeof(faviconIco)); - } -} - -void WebServer::httpSuccess(const char *contentType, - const char *extraHeaders) -{ - P(successMsg1) = - "HTTP/1.0 200 OK" CRLF - WEBDUINO_SERVER_HEADER - "Access-Control-Allow-Origin: *" CRLF - "Content-Type: "; - - printP(successMsg1); - print(contentType); - printCRLF(); - if (extraHeaders) { - print(extraHeaders); - printCRLF(); - } - printCRLF(); -} - -void WebServer::httpSeeOther(const char *otherURL) -{ - P(seeOtherMsg) = - "HTTP/1.0 303 See Other" CRLF - WEBDUINO_SERVER_HEADER - "Location: "; - - printP(seeOtherMsg); - print(otherURL); - printCRLF(); - printCRLF(); -} - -int WebServer::read() -{ - if (m_client == NULL) - return -1; - - if (m_pushbackDepth == 0) - { - unsigned long timeoutTime = millis() + WEBDUINO_READ_TIMEOUT_IN_MS; - - while (m_client.connected()) - { - // stop reading the socket early if we get to content-length - // characters in the POST. This is because some clients leave - // the socket open because they assume HTTP keep-alive. - if (m_readingContent) - { - if (m_contentLength == 0) - { -#if WEBDUINO_SERIAL_DEBUGGING > 1 - Serial.println("\n*** End of content, terminating connection"); -#endif - return -1; - } - --m_contentLength; - } - - int ch = m_client.read(); - - // if we get a character, return it, otherwise continue in while - // loop, checking connection status - if (ch != -1) - { -#if WEBDUINO_SERIAL_DEBUGGING - if (ch == '\r') - Serial.print(""); - else if (ch == '\n') - Serial.println(""); - else - Serial.print((char)ch); -#endif - return ch; - } - else - { - unsigned long now = millis(); - if (now > timeoutTime) - { - // connection timed out, destroy client, return EOF -#if WEBDUINO_SERIAL_DEBUGGING - Serial.println("*** Connection timed out"); -#endif - m_client.flush(); - m_client.stop(); - return -1; - } - } - } - - // connection lost, return EOF -#if WEBDUINO_SERIAL_DEBUGGING - Serial.println("*** Connection lost"); -#endif - return -1; - } - else - return m_pushback[--m_pushbackDepth]; -} - -void WebServer::push(int ch) -{ - // don't allow pushing EOF - if (ch == -1) - return; - - m_pushback[m_pushbackDepth++] = ch; - // can't raise error here, so just replace last char over and over - if (m_pushbackDepth == SIZE(m_pushback)) - m_pushbackDepth = SIZE(m_pushback) - 1; -} - -void WebServer::reset() -{ - m_pushbackDepth = 0; -} - -bool WebServer::expect(const char *str) -{ - const char *curr = str; - while (*curr != 0) - { - int ch = read(); - if (ch != *curr++) - { - // push back ch and the characters we accepted - push(ch); - while (--curr != str) - push(curr[-1]); - return false; - } - } - return true; -} - -bool WebServer::readInt(int &number) -{ - bool negate = false; - bool gotNumber = false; - int ch; - number = 0; - - // absorb whitespace - do - { - ch = read(); - } while (ch == ' ' || ch == '\t'); - - // check for leading minus sign - if (ch == '-') - { - negate = true; - ch = read(); - } - - // read digits to update number, exit when we find non-digit - while (ch >= '0' && ch <= '9') - { - gotNumber = true; - number = number * 10 + ch - '0'; - ch = read(); - } - - push(ch); - if (negate) - number = -number; - return gotNumber; -} - -bool WebServer::readPOSTparam(char *name, int nameLen, - char *value, int valueLen) -{ - // assume name is at current place in stream - int ch; - - // clear out name and value so they'll be NUL terminated - memset(name, 0, nameLen); - memset(value, 0, valueLen); - - // decrement length so we don't write into NUL terminator - --nameLen; - --valueLen; - - while ((ch = read()) != -1) - { - if (ch == '+') - { - ch = ' '; - } - else if (ch == '=') - { - /* that's end of name, so switch to storing in value */ - nameLen = 0; - continue; - } - else if (ch == '&') - { - /* that's end of pair, go away */ - return true; - } - else if (ch == '%') - { - /* handle URL encoded characters by converting back to original form */ - int ch1 = read(); - int ch2 = read(); - if (ch1 == -1 || ch2 == -1) - return false; - char hex[3] = { ch1, ch2, 0 }; - ch = strtoul(hex, NULL, 16); - } - - // check against 1 so we don't overwrite the final NUL - if (nameLen > 1) - { - *name++ = ch; - --nameLen; - } - else if (valueLen > 1) - { - *value++ = ch; - --valueLen; - } - } - - // if we get here, we hit the end-of-file, so POST is over and there - // are no more parameters - return false; -} - -/* Retrieve a parameter that was encoded as part of the URL, stored in - * the buffer pointed to by *tail. tail is updated to point just past - * the last character read from the buffer. */ -URLPARAM_RESULT WebServer::nextURLparam(char **tail, char *name, int nameLen, - char *value, int valueLen) -{ - // assume name is at current place in stream - char ch, hex[3]; - URLPARAM_RESULT result = URLPARAM_OK; - char *s = *tail; - bool keep_scanning = true; - bool need_value = true; - - // clear out name and value so they'll be NUL terminated - memset(name, 0, nameLen); - memset(value, 0, valueLen); - - if (*s == 0) - return URLPARAM_EOS; - // Read the keyword name - while (keep_scanning) - { - ch = *s++; - switch (ch) - { - case 0: - s--; // Back up to point to terminating NUL - // Fall through to "stop the scan" code - case '&': - /* that's end of pair, go away */ - keep_scanning = false; - need_value = false; - break; - case '+': - ch = ' '; - break; - case '%': - /* handle URL encoded characters by converting back - * to original form */ - if ((hex[0] = *s++) == 0) - { - s--; // Back up to NUL - keep_scanning = false; - need_value = false; - } - else - { - if ((hex[1] = *s++) == 0) - { - s--; // Back up to NUL - keep_scanning = false; - need_value = false; - } - else - { - hex[2] = 0; - ch = strtoul(hex, NULL, 16); - } - } - break; - case '=': - /* that's end of name, so switch to storing in value */ - keep_scanning = false; - break; - } - - - // check against 1 so we don't overwrite the final NUL - if (keep_scanning && (nameLen > 1)) - { - *name++ = ch; - --nameLen; - } - else - result = URLPARAM_NAME_OFLO; - } - - if (need_value && (*s != 0)) - { - keep_scanning = true; - while (keep_scanning) - { - ch = *s++; - switch (ch) - { - case 0: - s--; // Back up to point to terminating NUL - // Fall through to "stop the scan" code - case '&': - /* that's end of pair, go away */ - keep_scanning = false; - need_value = false; - break; - case '+': - ch = ' '; - break; - case '%': - /* handle URL encoded characters by converting back to original form */ - if ((hex[0] = *s++) == 0) - { - s--; // Back up to NUL - keep_scanning = false; - need_value = false; - } - else - { - if ((hex[1] = *s++) == 0) - { - s--; // Back up to NUL - keep_scanning = false; - need_value = false; - } - else - { - hex[2] = 0; - ch = strtoul(hex, NULL, 16); - } - - } - break; - } - - - // check against 1 so we don't overwrite the final NUL - if (keep_scanning && (valueLen > 1)) - { - *value++ = ch; - --valueLen; - } - else - result = (result == URLPARAM_OK) ? - URLPARAM_VALUE_OFLO : - URLPARAM_BOTH_OFLO; - } - } - *tail = s; - return result; -} - - - -// Read and parse the first line of the request header. -// The "command" (GET/HEAD/POST) is translated into a numeric value in type. -// The URL is stored in request, up to the length passed in length -// NOTE 1: length must include one byte for the terminating NUL. -// NOTE 2: request is NOT checked for NULL, nor length for a value < 1. -// Reading stops when the code encounters a space, CR, or LF. If the HTTP -// version was supplied by the client, it will still be waiting in the input -// stream when we exit. -// -// On return, length contains the amount of space left in request. If it's -// less than 0, the URL was longer than the buffer, and part of it had to -// be discarded. - -void WebServer::getRequest(WebServer::ConnectionType &type, - char *request, int *length) -{ - --*length; // save room for NUL - - type = INVALID; - - // store the GET/POST line of the request - if (expect("GET ")) - type = GET; - else if (expect("HEAD ")) - type = HEAD; - else if (expect("POST ")) - type = POST; - - // if it doesn't start with any of those, we have an unknown method - // so just eat rest of header - - int ch; - while ((ch = read()) != -1) - { - // stop storing at first space or end of line - if (ch == ' ' || ch == '\n' || ch == '\r') - { - break; - } - if (*length > 0) - { - *request = ch; - ++request; - --*length; - } - } - // NUL terminate - *request = 0; -} - -void WebServer::processHeaders() -{ - // look for two things: the Content-Length header and the double-CRLF - // that ends the headers. - - while (1) - { - if (expect("Content-Length:")) - { - readInt(m_contentLength); -#if WEBDUINO_SERIAL_DEBUGGING > 1 - Serial.print("\n*** got Content-Length of "); - Serial.print(m_contentLength); - Serial.print(" ***"); -#endif - continue; - } - - if (expect(CRLF CRLF)) - { - m_readingContent = true; - return; - } - - // no expect checks hit, so just absorb a character and try again - if (read() == -1) - { - return; - } - } -} - -void WebServer::outputCheckboxOrRadio(const char *element, const char *name, - const char *val, const char *label, - bool selected) -{ - P(cbPart1a) = "