diff --git a/README.md b/README.md index 67183e3..d280edd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ -# RestClient for Arduino +# RestClient for Arduino ESP8266 WiFi modules -HTTP Request library for Arduino and the Ethernet shield. +HTTP Request library for Arduino and the ESP8266 WiFi SOC modules + +This library now supports SSL! To use with SSL, you need to include the SHA1 fingerprint of the certificate of the site you are connecting to. You can get this by using a desktop browser and inspecting the SSL cert used at the site. Please note: this is FRAGILE, if the site updates their SSL, your code will break. But, there is not enough memory on the ESP8266 to store all the rool certs, so this is a working method. Se the example below. + +This library is derived almost entirely from the great work done here: https://github.com/csquared/arduino-restclient # Install @@ -10,19 +14,13 @@ where `~/Documents/Arduino` is your sketchbook directory. > cd ~/Documents/Arduino > mkdir libraries > cd libraries - > git clone https://github.com/csquared/arduino-restclient.git RestClient + > git clone https://github.com/dakaz/esp8266-restclient.git RestClient # Usage ### Include -You need to have the `Ethernet` library already included. - -```c++ -#include -#include -#include "RestClient.h" -``` +You need to have the `ESP8266` board support already included. ### RestClient(host/ip, [port]) @@ -38,50 +36,20 @@ Use a local IP and an explicit port: RestClient client = RestClient("192.168.1.50",5000); ``` -### dhcp() - -Sets up `EthernetClient` with a mac address of `DEADBEEFFEED`. Returns `true` or `false` to indicate if setting up DHCP -was successful or not - -```c++ - client.dhcp() -``` - -Note: you can have multiple RestClient objects but only need to call -this once. - -Note: if you have multiple Arduinos on the same network, you'll need -to give each one a different mac address. - -### begin(byte mac[]) - -It just wraps the `EthernetClient` call to `begin` and DHCPs. -Use this if you need to explicitly set the mac address. - +Use a local IP, an explicit port to an SSL site and (must include the 1 to turn on SSL): ```c++ - byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; - if (client.begin(mac) == 0) { - Serial.println("Failed to configure Ethernet using DHCP"); - } +RestClient client = RestClient("www.kudoso.com",443, 1); ``` -### Manual Ethernet Setup - -You can skip the above methods and just configure the EthernetClient yourself: - +Use a local IP, an explicit port to an SSL site and verify the certificate with its fingerprint: ```c++ - byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; - //the IP address for the shield: - byte ip[] = { 192, 168, 2, 11 }; - Ethernet.begin(mac,ip); +RestClient client = RestClient("www.kudoso.com",443, "EE 16 77 79 55 58 92 46 FB 18 40 99 2E 17 7E AB 32 0A 4A 88"); ``` -```c++ - byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; - Ethernet.begin(mac); -``` +### dhcp() -This is especially useful for debugging network connection issues. +Sets up `EthernetClient` with a mac address of `DEADBEEFFEED`. Returns `true` or `false` to indicate if setting up DHCP +was successful or not ## RESTful methods diff --git a/RestClient.cpp b/RestClient.cpp index 7afd641..a289639 100644 --- a/RestClient.cpp +++ b/RestClient.cpp @@ -9,17 +9,25 @@ #endif RestClient::RestClient(const char* _host){ - host = _host; - port = 80; - num_headers = 0; - contentType = "application/x-www-form-urlencoded"; // default + host = _host; + port = 80; + ssl = 0; + fingerprint = NULL; + num_headers = 0; + if (!contentType) { + contentType = "application/x-www-form-urlencoded"; // default + } } RestClient::RestClient(const char* _host, int _port){ - host = _host; - port = _port; - num_headers = 0; - contentType = "application/x-www-form-urlencoded"; // default + host = _host; + port = _port; + ssl = 0; + fingerprint = NULL; + num_headers = 0; + if (!contentType) { + contentType = "application/x-www-form-urlencoded"; // default + } } bool RestClient::dhcp(){ @@ -34,120 +42,171 @@ bool RestClient::dhcp(){ return true; } -int RestClient::begin(byte mac[]){ - return Ethernet.begin(mac); - //give it time to initialize - delay(1000); +RestClient::RestClient(const char* _host, int _port, const char* _fingerprint){ + host = _host; + port = _port; + ssl = 1; + fingerprint = _fingerprint; + num_headers = 0; + if (!contentType) { + contentType = "application/x-www-form-urlencoded"; // default + } +} + +RestClient::RestClient(const char* _host, int _port, int _ssl) { + host = _host; + port = _port; + ssl = (_ssl) ? 1 : 0; + fingerprint = NULL; + num_headers = 0; + if (!contentType) { + contentType = "application/x-www-form-urlencoded"; // default + } } // GET path int RestClient::get(const char* path){ - return request("GET", path, NULL, NULL); + return request("GET", path, NULL, NULL); } //GET path with response int RestClient::get(const char* path, String* response){ - return request("GET", path, NULL, response); + return request("GET", path, NULL, response); } // POST path and body int RestClient::post(const char* path, const char* body){ - return request("POST", path, body, NULL); + return request("POST", path, body, NULL); } // POST path and body with response int RestClient::post(const char* path, const char* body, String* response){ - return request("POST", path, body, response); + return request("POST", path, body, response); +} + +// PATCH path and body +int RestClient::patch(const char* path, const char* body){ + return request("PATCH", path, body, NULL); +} + +// PATCH path and body with response +int RestClient::patch(const char* path, const char* body, String* response){ + return request("PATCH", path, body, response); } // PUT path and body int RestClient::put(const char* path, const char* body){ - return request("PUT", path, body, NULL); + return request("PUT", path, body, NULL); } // PUT path and body with response int RestClient::put(const char* path, const char* body, String* response){ - return request("PUT", path, body, response); + return request("PUT", path, body, response); } // DELETE path int RestClient::del(const char* path){ - return request("DELETE", path, NULL, NULL); + return request("DELETE", path, NULL, NULL); } // DELETE path and response int RestClient::del(const char* path, String* response){ - return request("DELETE", path, NULL, response); + return request("DELETE", path, NULL, response); } // DELETE path and body int RestClient::del(const char* path, const char* body ){ - return request("DELETE", path, body, NULL); + return request("DELETE", path, body, NULL); } // DELETE path and body with response int RestClient::del(const char* path, const char* body, String* response){ - return request("DELETE", path, body, response); + return request("DELETE", path, body, response); } void RestClient::write(const char* string){ - HTTP_DEBUG_PRINT(string); - client.print(string); + + if(ssl) { + HTTP_DEBUG_PRINT("\nSSL Print: "); + HTTP_DEBUG_PRINT(string); + sslClient.print(string); + } else { + HTTP_DEBUG_PRINT("\nHTTP Print: "); + HTTP_DEBUG_PRINT(string); + client.print(string); + } } void RestClient::setHeader(const char* header){ - headers[num_headers] = header; - num_headers++; + headers[num_headers] = header; + num_headers++; } void RestClient::setContentType(const char* contentTypeValue){ - contentType = contentTypeValue; + contentType = contentTypeValue; +} + +void RestClient::setSSL(int _ssl){ + ssl = (_ssl) ? 1 : 0; } // The mother- generic request method. // int RestClient::request(const char* method, const char* path, - const char* body, String* response){ + const char* body, String* response){ + + HTTP_DEBUG_PRINT("HTTP: connect\n"); + + if (ssl) { + if(!sslClient.connect(host, port)){ + HTTP_DEBUG_PRINT("HTTPS Connection failed\n"); + return 0; + } + if (fingerprint) { + HTTP_DEBUG_PRINT("Verifiying SSL certificate\n"); + if (sslClient.verify(fingerprint, host)) { + HTTP_DEBUG_PRINT("SSL certificate matches\n"); + } else { + HTTP_DEBUG_PRINT("SSL certificate does not match\n"); + return 0; + } + } + } else { + if(!client.connect(host, port)){ + HTTP_DEBUG_PRINT("HTTP Connection failed\n"); + return 0; + } + } - HTTP_DEBUG_PRINT("HTTP: connect\n"); - if(client.connect(host, port)){ HTTP_DEBUG_PRINT("HTTP: connected\n"); HTTP_DEBUG_PRINT("REQUEST: \n"); - // Make a HTTP request line: - write(method); - write(" "); - write(path); - write(" HTTP/1.1\r\n"); + + String request = String(method) + " " + String(path) + " HTTP/1.1\r\n"; for(int i=0; iconcat(c); - } - else - { - if (c == '\n' && currentLineIsBlank) { - httpBody = true; - } - - if (c == '\n') { - // you're starting a new line - currentLineIsBlank = true; - } - else if (c != '\r') { - // you've gotten a character on the current line - currentLineIsBlank = false; - } - } + HTTP_DEBUG_PRINT("HTTP: RESPONSE: \n"); + void* http_client; + if(ssl) { + HTTP_DEBUG_PRINT("HTTP: Connect: " + String(sslClient.connected()) + " Available: " + String(sslClient.available()) + "\n"); + while (sslClient.connected()) { + HTTP_DEBUG_PRINT("."); + + if (sslClient.available()) { + HTTP_DEBUG_PRINT(","); + + char c = sslClient.read(); + HTTP_DEBUG_PRINT(c); + + if(c == ' ' && !inStatus){ + inStatus = true; + } + + if(inStatus && i < 3 && c != ' '){ + statusCode[i] = c; + i++; + } + if(i == 3){ + statusCode[i] = '\0'; + code = atoi(statusCode); + } + + if(httpBody){ + //only write response if its not null + if(response != NULL) response->concat(c); + } + else + { + if (c == '\n' && currentLineIsBlank) { + httpBody = true; + } + + if (c == '\n') { + // you're starting a new line + currentLineIsBlank = true; + } + else if (c != '\r') { + // you've gotten a character on the current line + currentLineIsBlank = false; + } + } + } + } + HTTP_DEBUG_PRINT("HTTPS client closed \n"); + }else { + while (client.connected()) { + HTTP_DEBUG_PRINT("."); + + if (client.available()) { + HTTP_DEBUG_PRINT(","); + + char c = client.read(); + HTTP_DEBUG_PRINT(c); + + if(c == ' ' && !inStatus){ + inStatus = true; + } + + if(inStatus && i < 3 && c != ' '){ + statusCode[i] = c; + i++; + } + if(i == 3){ + statusCode[i] = '\0'; + code = atoi(statusCode); + } + + if(httpBody){ + //only write response if its not null + if(response != NULL) response->concat(c); + } + else + { + if (c == '\n' && currentLineIsBlank) { + httpBody = true; + } + + if (c == '\n') { + // you're starting a new line + currentLineIsBlank = true; + } + else if (c != '\r') { + // you've gotten a character on the current line + currentLineIsBlank = false; + } + } + } + } } - } - HTTP_DEBUG_PRINT("HTTP: return readResponse3\n"); - return code; + HTTP_DEBUG_PRINT("HTTP: return readResponse3\n"); + return code; } diff --git a/RestClient.h b/RestClient.h index 0a13101..176a13b 100644 --- a/RestClient.h +++ b/RestClient.h @@ -1,12 +1,19 @@ +#ifndef RestClient_h +#define RestClient_h + #include -#include -#include +#include +#include class RestClient { - public: +public: RestClient(const char* host); RestClient(const char* _host, int _port); + // set ssl to on but do not verify server identity with fingerprint + RestClient(const char* _host, int _port, int _ssl); + // set fingerprint if using SSL, stores the SHA1 fingerprint of the remote site, implicity sets ssl to on + RestClient(const char* _host, int _port, const char* _fingerprint); //Client Setup bool dhcp(); @@ -19,6 +26,8 @@ class RestClient { void setHeader(const char*); // Set Content-Type Header void setContentType(const char*); + // Set SSL support on(1) or off(0) + void setSSL(int); // GET path int get(const char*); @@ -30,6 +39,11 @@ class RestClient { // POST path and body and response int post(const char* path, const char* body, String*); + // PATCH path and body + int patch(const char* path, const char* body); + // PATCH path and body and response + int patch(const char* path, const char* body, String*); + // PUT path and body int put(const char* path, const char* body); // PUT path and body and response @@ -44,13 +58,18 @@ class RestClient { // DELETE path and body and response int del(const char*, const char*, String*); - private: - EthernetClient client; +private: + WiFiClient client; + WiFiClientSecure sslClient; int readResponse(String*); void write(const char*); const char* host; int port; int num_headers; const char* headers[10]; - const char* contentType; + const char* contentType; + const char* fingerprint; + int ssl; }; + +#endif