diff --git a/cores/esp8266/base64.cpp b/cores/esp8266/base64.cpp new file mode 100644 index 000000000..11deb0af0 --- /dev/null +++ b/cores/esp8266/base64.cpp @@ -0,0 +1,63 @@ +/** + * base64.cpp + * + * Created on: 09.12.2015 + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the ESP8266 core for Arduino. + * + * 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 "Arduino.h" +extern "C" { +#include "libb64/cdecode.h" +#include "libb64/cencode.h" +} +#include "base64.h" + +/** + * convert input data to base64 + * @param data uint8_t * + * @param length size_t + * @return String + */ +String base64::encode(uint8_t * data, size_t length) { + // base64 needs more size then the source data + size_t size = ((length * 1.6f) + 1); + char * buffer = (char *) malloc(size); + if(buffer) { + base64_encodestate _state; + base64_init_encodestate(&_state); + int len = base64_encode_block((const char *) &data[0], length, &buffer[0], &_state); + len = base64_encode_blockend((buffer + len), &_state); + + String base64 = String(buffer); + free(buffer); + return base64; + } + return String("-FAIL-"); +} + +/** + * convert input data to base64 + * @param text String + * @return String + */ +String base64::encode(String text) { + return base64::encode((uint8_t *) text.c_str(), text.length()); +} + diff --git a/cores/esp8266/base64.h b/cores/esp8266/base64.h new file mode 100644 index 000000000..39be2d397 --- /dev/null +++ b/cores/esp8266/base64.h @@ -0,0 +1,36 @@ +/** + * base64.h + * + * Created on: 09.12.2015 + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the ESP8266 core for Arduino. + * + * 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 CORE_BASE64_H_ +#define CORE_BASE64_H_ + +class base64 { + public: + static String encode(uint8_t * data, size_t length); + static String encode(String text); + private: +}; + + +#endif /* CORE_BASE64_H_ */ diff --git a/cores/esp8266/core_esp8266_features.h b/cores/esp8266/core_esp8266_features.h index bafcd3aee..ae5d58539 100644 --- a/cores/esp8266/core_esp8266_features.h +++ b/cores/esp8266/core_esp8266_features.h @@ -26,6 +26,7 @@ #define CORE_HAS_LIBB64 +#define CORE_HAS_BASE64_CLASS #endif diff --git a/libraries/ESP8266HTTPClient/examples/Authorization/Authorization.ino b/libraries/ESP8266HTTPClient/examples/Authorization/Authorization.ino new file mode 100644 index 000000000..79a4c3fdf --- /dev/null +++ b/libraries/ESP8266HTTPClient/examples/Authorization/Authorization.ino @@ -0,0 +1,84 @@ +/** + * Authorization.ino + * + * Created on: 09.12.2015 + * + */ + +#include + +#include +#include + +#include + +#define USE_SERIAL Serial + +ESP8266WiFiMulti WiFiMulti; + +void setup() { + + USE_SERIAL.begin(115200); + // USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "PASSWORD"); + +} + +void loop() { + // wait for WiFi connection + if((WiFiMulti.run() == WL_CONNECTED)) { + + HTTPClient http; + + USE_SERIAL.print("[HTTP] begin...\n"); + // configure traged server and url + + + http.begin("http://user:password@192.168.1.12/test.html"); + + /* + // or + http.begin("http://192.168.1.12/test.html"); + http.setAuthorization("user", "password"); + + // or + http.begin("http://192.168.1.12/test.html"); + http.setAuthorization("dXNlcjpwYXN3b3Jk"); + */ + + + USE_SERIAL.print("[HTTP] GET...\n"); + // start connection and send HTTP header + int httpCode = http.GET(); + + // httpCode will be negative on error + if(httpCode) { + // HTTP header has been send and Server response header has been handled + USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode); + + // file found at server + if(httpCode == HTTP_CODE_OK) { + String payload = http.getString(); + USE_SERIAL.println(payload); + } + } else { + USE_SERIAL.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); + } + + http.end(); + } + + delay(10000); +} + diff --git a/libraries/ESP8266HTTPClient/examples/BasicHttpClient/BasicHttpClient.ino b/libraries/ESP8266HTTPClient/examples/BasicHttpClient/BasicHttpClient.ino index 3c722556b..d3a5fbc7d 100644 --- a/libraries/ESP8266HTTPClient/examples/BasicHttpClient/BasicHttpClient.ino +++ b/libraries/ESP8266HTTPClient/examples/BasicHttpClient/BasicHttpClient.ino @@ -43,24 +43,28 @@ void loop() { USE_SERIAL.print("[HTTP] begin...\n"); // configure traged server and url - //http.begin("192.168.1.12", 443, "/test.html", true, "7a 9c f4 db 40 d3 62 5a 6e 21 bc 5c cc 66 c8 3e a1 45 59 38"); //HTTPS - http.begin("192.168.1.12", 80, "/test.html"); //HTTP + //http.begin("https://192.168.1.12/test.html", "7a 9c f4 db 40 d3 62 5a 6e 21 bc 5c cc 66 c8 3e a1 45 59 38"); //HTTPS + http.begin("http://192.168.1.12/test.html"); //HTTP USE_SERIAL.print("[HTTP] GET...\n"); // start connection and send HTTP header int httpCode = http.GET(); + + // httpCode will be negative on error if(httpCode) { // HTTP header has been send and Server response header has been handled USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode); // file found at server - if(httpCode == 200) { + if(httpCode == HTTP_CODE_OK) { String payload = http.getString(); USE_SERIAL.println(payload); } } else { - USE_SERIAL.print("[HTTP] GET... failed, no connection or no HTTP server\n"); + USE_SERIAL.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); } + + http.end(); } delay(10000); diff --git a/libraries/ESP8266HTTPClient/examples/ReuseConnection/ReuseConnection.ino b/libraries/ESP8266HTTPClient/examples/ReuseConnection/ReuseConnection.ino index e725a1f76..35babdc33 100644 --- a/libraries/ESP8266HTTPClient/examples/ReuseConnection/ReuseConnection.ino +++ b/libraries/ESP8266HTTPClient/examples/ReuseConnection/ReuseConnection.ino @@ -36,26 +36,31 @@ void setup() { WiFiMulti.addAP("SSID", "PASSWORD"); - + // allow reuse (if server supports it) + http.setReuse(true); } void loop() { // wait for WiFi connection if((WiFiMulti.run() == WL_CONNECTED)) { - http.begin("192.168.1.12", 80, "/test.html"); + http.begin("http://192.168.1.12/test.html"); + //http.begin("192.168.1.12", 80, "/test.html"); int httpCode = http.GET(); if(httpCode) { USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode); // file found at server - if(httpCode == 200) { + if(httpCode == HTTP_CODE_OK) { http.writeToStream(&USE_SERIAL); } } else { - USE_SERIAL.print("[HTTP] GET... failed, no connection or no HTTP server\n"); + USE_SERIAL.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); } + + http.end(); + } delay(1000); diff --git a/libraries/ESP8266HTTPClient/examples/StreamHttpClient/StreamHttpClient.ino b/libraries/ESP8266HTTPClient/examples/StreamHttpClient/StreamHttpClient.ino index 442fa6547..2e7f4487d 100644 --- a/libraries/ESP8266HTTPClient/examples/StreamHttpClient/StreamHttpClient.ino +++ b/libraries/ESP8266HTTPClient/examples/StreamHttpClient/StreamHttpClient.ino @@ -42,19 +42,20 @@ void loop() { HTTPClient http; USE_SERIAL.print("[HTTP] begin...\n"); - // configure traged server and url - http.begin("192.168.1.12", 80, "/test.html"); + + // configure server and url + http.begin("http://192.168.1.12/test.html"); + //http.begin("192.168.1.12", 80, "/test.html"); USE_SERIAL.print("[HTTP] GET...\n"); // start connection and send HTTP header int httpCode = http.GET(); if(httpCode) { // HTTP header has been send and Server response header has been handled - USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode); // file found at server - if(httpCode == 200) { + if(httpCode == HTTP_CODE_OK) { // get lenght of document (is -1 when Server sends no Content-Length header) int len = http.getSize(); @@ -89,8 +90,10 @@ void loop() { } } else { - USE_SERIAL.print("[HTTP] GET... failed, no connection or no HTTP server\n"); + USE_SERIAL.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); } + + http.end(); } delay(10000); diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp index 37b71dbc1..6a8d010e1 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp @@ -26,9 +26,11 @@ #include #include #include +#include #include "ESP8266HTTPClient.h" + /** * constractor */ @@ -100,27 +102,34 @@ void HTTPClient::begin(String url, String httpsFingerprint) { String protocol; // check for : (http: or https: int index = url.indexOf(':'); - int index2; + //int index2; bool hasPort = false; if(index) { protocol = url.substring(0, index); url.remove(0, (index + 3)); // remove http:// or https:// - index = url.indexOf(':'); - index2 = url.indexOf('/'); + index = url.indexOf('/'); + String host = url.substring(0, index); + url.remove(0, index); // remove host part - if(index >= 0 && ((index2 >= 0 && index < index2) || index2 == 0)) { // do we have a port? - _host = url.substring(0, index); // hostname - url.remove(0, (index + 1)); // remove hostname + : + // get Authorization + index = host.indexOf('@'); + if(index >= 0) { + // auth info + String auth = host.substring(0, index); + host.remove(0, index +1); // remove auth part including @ + _base64Authorization = base64::encode(auth); + } - index = url.indexOf('/'); - _port = url.substring(0, index).toInt(); // get port - url.remove(0, index); // remove port + // get port + index = host.indexOf(':'); + if(index >= 0) { + _host = host.substring(0, index); // hostname + host.remove(0, (index + 1)); // remove hostname + : + _port = host.toInt(); // get port hasPort = true; } else { - index = index2; - _host = url.substring(0, index); - url.remove(0, index); // remove hostname + _host = host; } _url = url; @@ -139,6 +148,7 @@ void HTTPClient::begin(String url, String httpsFingerprint) { DEBUG_HTTPCLIENT("[HTTP-Client][begin] protocol: %s unknown?!\n", protocol.c_str()); return; } + } DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port: %d url: %s https: %d httpsFingerprint: %s\n", _host.c_str(), _port, _url.c_str(), _https, _httpsFingerprint.c_str()); @@ -219,6 +229,30 @@ void HTTPClient::setUserAgent(const char * userAgent) { _userAgent = userAgent; } +/** + * set the Authorizatio for the http request + * @param user const char * + * @param password const char * + */ +void HTTPClient::setAuthorization(const char * user, const char * password) { + if(user && password) { + String auth = user; + auth += ":"; + auth += password; + _base64Authorization = base64::encode(auth); + } +} + +/** + * set the Authorizatio for the http request + * @param auth const char * base64 + */ +void HTTPClient::setAuthorization(const char * auth) { + if(auth) { + _base64Authorization = auth; + } +} + /** * send a GET request * @return http code @@ -454,6 +488,33 @@ String HTTPClient::getString(void) { return sstring; } +/** + * converts error code to String + * @param error int + * @return String + */ +String HTTPClient::errorToString(int error) { + switch(error) { + case HTTPC_ERROR_CONNECTION_REFUSED: + return String("connection refused"); + case HTTPC_ERROR_SEND_HEADER_FAILED: + return String("send header failed"); + case HTTPC_ERROR_SEND_PAYLOAD_FAILED: + return String("send payload failed"); + case HTTPC_ERROR_NOT_CONNECTED: + return String("not connected"); + case HTTPC_ERROR_CONNECTION_LOST: + return String("connection lost"); + case HTTPC_ERROR_NO_STREAM: + return String("no stream"); + case HTTPC_ERROR_NO_HTTP_SERVER: + return String("no HTTP server"); + default: + return String(); + } +} + + /** * adds Header to the request * @param name @@ -463,7 +524,7 @@ String HTTPClient::getString(void) { void HTTPClient::addHeader(const String& name, const String& value, bool first) { // not allow set of Header handled by code - if(!name.equalsIgnoreCase("Connection") && !name.equalsIgnoreCase("User-Agent") && !name.equalsIgnoreCase("Host")) { + if(!name.equalsIgnoreCase("Connection") && !name.equalsIgnoreCase("User-Agent") && !name.equalsIgnoreCase("Host") && !(_base64Authorization.length() && name.equalsIgnoreCase("Authorization"))) { String headerLine = name; headerLine += ": "; headerLine += value; @@ -595,7 +656,13 @@ bool HTTPClient::sendHeader(const char * type) { } else { header += "close"; } - header += "\r\n" + _Headers + "\r\n"; + header += "\r\n"; + + if(_base64Authorization.length()) { + header += "Authorization: Basic " + _base64Authorization + "\r\n"; + } + + header += _Headers + "\r\n"; return (_tcp->write(header.c_str(), header.length()) == header.length()); } diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h index 2d1312a29..2bf6d0565 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h @@ -42,6 +42,67 @@ #define HTTPC_ERROR_NO_STREAM (-6) #define HTTPC_ERROR_NO_HTTP_SERVER (-7) +/// HTTP codes see RFC7231 +typedef enum { + HTTP_CODE_CONTINUE = 100, + HTTP_CODE_SWITCHING_PROTOCOLS = 101, + HTTP_CODE_PROCESSING = 102, + HTTP_CODE_OK = 200, + HTTP_CODE_CREATED = 201, + HTTP_CODE_ACCEPTED = 202, + HTTP_CODE_NON_AUTHORITATIVE_INFORMATION = 203, + HTTP_CODE_NO_CONTENT = 204, + HTTP_CODE_RESET_CONTENT = 205, + HTTP_CODE_PARTIAL_CONTENT = 206, + HTTP_CODE_MULTI_STATUS = 207, + HTTP_CODE_ALREADY_REPORTED = 208, + HTTP_CODE_IM_USED = 226, + HTTP_CODE_MULTIPLE_CHOICES = 300, + HTTP_CODE_MOVED_PERMANENTLY = 301, + HTTP_CODE_FOUND = 302, + HTTP_CODE_SEE_OTHER = 303, + HTTP_CODE_NOT_MODIFIED = 304, + HTTP_CODE_USE_PROXY = 305, + HTTP_CODE_TEMPORARY_REDIRECT = 307, + HTTP_CODE_PERMANENT_REDIRECT = 308, + HTTP_CODE_BAD_REQUEST = 400, + HTTP_CODE_UNAUTHORIZED = 401, + HTTP_CODE_PAYMENT_REQUIRED = 402, + HTTP_CODE_FORBIDDEN = 403, + HTTP_CODE_NOT_FOUND = 404, + HTTP_CODE_METHOD_NOT_ALLOWED = 405, + HTTP_CODE_NOT_ACCEPTABLE = 406, + HTTP_CODE_PROXY_AUTHENTICATION_REQUIRED = 407, + HTTP_CODE_REQUEST_TIMEOUT = 408, + HTTP_CODE_CONFLICT = 409, + HTTP_CODE_GONE = 410, + HTTP_CODE_LENGTH_REQUIRED = 411, + HTTP_CODE_PRECONDITION_FAILED = 412, + HTTP_CODE_PAYLOAD_TOO_LARGE = 413, + HTTP_CODE_URI_TOO_LONG = 414, + HTTP_CODE_UNSUPPORTED_MEDIA_TYPE = 415, + HTTP_CODE_RANGE_NOT_SATISFIABLE = 416, + HTTP_CODE_EXPECTATION_FAILED = 417, + HTTP_CODE_MISDIRECTED_REQUEST = 421, + HTTP_CODE_UNPROCESSABLE_ENTITY = 422, + HTTP_CODE_LOCKED = 423, + HTTP_CODE_FAILED_DEPENDENCY = 424, + HTTP_CODE_UPGRADE_REQUIRED = 426, + HTTP_CODE_PRECONDITION_REQUIRED = 428, + HTTP_CODE_TOO_MANY_REQUESTS = 429, + HTTP_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE = 431, + HTTP_CODE_INTERNAL_SERVER_ERROR = 500, + HTTP_CODE_NOT_IMPLEMENTED = 501, + HTTP_CODE_BAD_GATEWAY = 502, + HTTP_CODE_SERVICE_UNAVAILABLE = 503, + HTTP_CODE_GATEWAY_TIMEOUT = 504, + HTTP_CODE_HTTP_VERSION_NOT_SUPPORTED = 505, + HTTP_CODE_VARIANT_ALSO_NEGOTIATES = 506, + HTTP_CODE_INSUFFICIENT_STORAGE = 507, + HTTP_CODE_LOOP_DETECTED = 508, + HTTP_CODE_NOT_EXTENDED = 510, + HTTP_CODE_NETWORK_AUTHENTICATION_REQUIRED = 511 +} t_http_codes; class HTTPClient { public: @@ -60,6 +121,8 @@ class HTTPClient { void setReuse(bool reuse); /// keep-alive void setUserAgent(const char * userAgent); + void setAuthorization(const char * user, const char * password); + void setAuthorization(const char * auth); /// request handling int GET(); @@ -86,6 +149,8 @@ class HTTPClient { int writeToStream(Stream * stream); String getString(void); + String errorToString(int error); + protected: struct RequestArgument { @@ -109,6 +174,7 @@ class HTTPClient { String _Headers; String _userAgent; + String _base64Authorization; /// Response handling RequestArgument* _currentHeaders;