diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/BridgeHttpClient.cpp b/BridgeHttpClient.cpp new file mode 100644 index 0000000..f81a3b2 --- /dev/null +++ b/BridgeHttpClient.cpp @@ -0,0 +1,145 @@ +/* + Copyright (c) 2016 Imre Horvath. 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 "BridgeHttpClient.h" + +void BridgeHttpClient::get(const char *url) { + request("GET", url, NULL); +} + +void BridgeHttpClient::post(const char *url, const char *data) { + request("POST", url, data); +} + +void BridgeHttpClient::put(const char *url, const char *data) { + request("PUT", url, data); +} + +void BridgeHttpClient::del(const char *url) { + request("DELETE", url, NULL); +} + +unsigned int BridgeHttpClient::getResponseCode() { + Process p; + p.runShellCommand("head -n 1 " + tempFileName + " | cut -d' ' -f2"); + return p.parseInt(); +} + +String BridgeHttpClient::getResponseHeaders() { + String responseHeaders; + + Process p; + p.runShellCommand("tail -n +2 " + tempFileName); + + while (p.available() > 0) { + char c = p.read(); + responseHeaders += c; + } + + responseHeaders.trim(); + return responseHeaders; +} + +void BridgeHttpClient::getAsync(const char *url) { + request("GET", url, NULL, true); +} + +void BridgeHttpClient::postAsync(const char *url, const char *data) { + request("POST", url, data, true); +} + +void BridgeHttpClient::putAsync(const char *url, const char *data) { + request("PUT", url, data, true); +} + +void BridgeHttpClient::delAsync(const char *url) { + request("DELETE", url, NULL, true); +} + +int BridgeHttpClient::addHeader(const char *header) { + if (headerIndex < HEADERCNT) { + headers[headerIndex++] = header; + return 0; + } + return -1; +} + +void BridgeHttpClient::basicAuth(const char *user, const char *passwd) { + this->user = user; + this->passwd = passwd; +} + +String BridgeHttpClient::getResponseHeaderValue(const String& header) { + if (cachedRespHeaders.length() == 0) { + cachedRespHeaders = getResponseHeaders(); + } + int startOfValue = cachedRespHeaders.indexOf(':', cachedRespHeaders.indexOf(header)) + 1; + String respValue = cachedRespHeaders.substring(startOfValue, + cachedRespHeaders.indexOf('\n', startOfValue)); + respValue.trim(); + return respValue; +} + +void BridgeHttpClient::request(const char *verb, const char *url, const char *data, bool async) { + Process p; + p.runShellCommand("mktemp"); + tempFileName = p.readStringUntil('\n'); + + clearCachedRespHeaders(); + + begin("curl"); + addParameter("-D"); + addParameter(tempFileName); + + if (verb != "GET") { + addParameter("-X"); + addParameter(verb); + } + + if (isInsecureEnabled) { + addParameter("-k"); + } + + if (user && passwd) { + String auth; + auth += user; + auth += ":"; + auth += passwd; + + addParameter("-u"); + addParameter(auth); + } + + for (int i = 0; i < headerIndex; i++) { + addParameter("-H"); + addParameter(headers[i]); + } + + if (data != NULL) { + addParameter("-d"); + addParameter(data); + } + + addParameter(url); + + if (async) { + runAsynchronously(); + } else { + (void) run(); + } +} diff --git a/BridgeHttpClient.h b/BridgeHttpClient.h new file mode 100644 index 0000000..22e8eb6 --- /dev/null +++ b/BridgeHttpClient.h @@ -0,0 +1,117 @@ +/* + Copyright (c) 2016 Imre Horvath. 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 _BRIDGE_HTTP_CLIENT_H_ +#define _BRIDGE_HTTP_CLIENT_H_ + +#include + +class BridgeHttpClient : public Process { + + public: + BridgeHttpClient() : isInsecureEnabled(false), + user(NULL), passwd(NULL), + headerIndex(0) {} + + void get(const char *url); + void post(const char *url, const char *data); + void put(const char *url, const char *data); + void del(const char *url); + + /** + * Call this method after the request has finished, + * to get the HTTP response code from the server. + * + * Returns the response code eg. 200 + */ + unsigned int getResponseCode(); + + /** + * Call this method after the request has finished, + * to get the response headers received from the server. + * + * Returns a String object with all the response headers included. + */ + String getResponseHeaders(); + + // unsigned int exitValue(); // Method in superclass to retrieve the exit status + // boolean running(); // Method in superclass which can be used to check if the async request is still running or has finished. + + void getAsync(const char *url); + void postAsync(const char *url, const char *data); + void putAsync(const char *url, const char *data); + void delAsync(const char *url); + + /** + * Call this method before issuing the requests, + * to allows "insecure" SSL. + * + * Useful in the following case for example: + * Certificate cannot be authenticated with known CA certificates. + */ + void enableInsecure() { isInsecureEnabled = true; } + + /** + * Call this method before issuing the request, + * to add an extra header to the request. + * + * Returns 0 if the header fits into the array of headers. -1 otherwise. + */ + int addHeader(const char *header); + + /** + * Call this method before issuing the request, + * to include basic authorization into the request. + */ + void basicAuth(const char *user, const char *passwd); + + /** + * Call this method between the different request calls on the same client, + * to clear/setup the request headers for the next call. + */ + void clearHeaders() { headerIndex = 0; } + + /** + * Call this method between the different request calls on the same client, + * to clear the previously set basic authorization for the subsequent call. + */ + void clearAuth() { user = passwd = NULL; } + + /** + * Call this method after the request has finished, + * to get a particular response header value. + * + * Returns a String object representing the response header value + */ + String getResponseHeaderValue(const String& header); + + private: + String tempFileName; + bool isInsecureEnabled; + const char *user; + const char *passwd; + static const int HEADERCNT = 10; // We handle max. 10 extra request headers + const char *headers[HEADERCNT]; + int headerIndex; + String cachedRespHeaders; + + void request(const char *verb, const char *url, const char *data, bool async=false); + void clearCachedRespHeaders() { cachedRespHeaders = ""; } +}; + +#endif diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..b8d3f92 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,15 @@ +Copyright (c) 2016 Imre Horvath. 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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..1b1d115 --- /dev/null +++ b/README.md @@ -0,0 +1,98 @@ +A practical and easy to use generic HTTP client library for the Yun +=================================================================== + +Features +-------- + +* GET/POST/PUT/DELETE +* Both sync and async requests +* You can access the HTTP response code and headers, not just the body +* Basic authorization +* Multiple extra request headers +* Extraction of a particular response header value +* Designed for efficiency and easy usage +* Built on top of the Bridge library +* Uses cURL on the Linux side + +API +--- + +```c++ +void get(const char *url); +void post(const char *url, const char *data); +void put(const char *url, const char *data); +void del(const char *url); + +/** + * Call this method after the request has finished, + * to get the HTTP response code from the server. + * + * Returns the response code eg. 200 + */ +unsigned int getResponseCode(); + +/** + * Call this method after the request has finished, + * to get the response headers received from the server. + * + * Returns a String object with all the response headers included. + */ +String getResponseHeaders(); + +// unsigned int exitValue(); // Method in superclass to retrieve the exit status +// boolean running(); // Method in superclass which can be used to check if the async request is still running or has finished. + +void getAsync(const char *url); +void postAsync(const char *url, const char *data); +void putAsync(const char *url, const char *data); +void delAsync(const char *url); + +/** + * Call this method before issuing the requests, + * to allows "insecure" SSL. + * + * Useful in the following case for example: + * Certificate cannot be authenticated with known CA certificates. + */ +void enableInsecure() { isInsecureEnabled = true; } + +/** + * Call this method before issuing the request, + * to add an extra header to the request. + * + * Returns 0 if the header fits into the array of headers. -1 otherwise. + */ +int addHeader(const char *header); + +/** + * Call this method before issuing the request, + * to include basic authorization into the request. + */ +void basicAuth(const char *user, const char *passwd); + +/** + * Call this method between the different request calls on the same client, + * to clear/setup the request headers for the next call. + */ +void clearHeaders() { headerIndex = 0; } + +/** + * Call this method between the different request calls on the same client, + * to clear the previously set basic authorization for the subsequent call. + */ +void clearAuth() { user = passwd = NULL; } + +/** + * Call this method after the request has finished, + * to get a particular response header value. + * + * Returns a String object representing the response header value + */ +String getResponseHeaderValue(const String& header); +``` + +TODO +---- + +* Add proxy support +* Provide more examples diff --git a/examples/ApiKey/ApiKey.ino b/examples/ApiKey/ApiKey.ino new file mode 100644 index 0000000..6044435 --- /dev/null +++ b/examples/ApiKey/ApiKey.ino @@ -0,0 +1,41 @@ + +#include +#include + +void setup() { + pinMode(13, OUTPUT); + digitalWrite(13, LOW); + Bridge.begin(); // Initialize Bridge + digitalWrite(13, HIGH); + + SerialUSB.begin(9600); + while (!SerialUSB); // wait for a serial connection + + BridgeHttpClient client; + client.addHeader("X-Api-Key: 12345"); + client.addHeader("Accept: application/json"); + + client.enableInsecure(); // Using HTTPS and peer cert. will not be able to auth. + + client.get("https://httpbin.org/headers"); + + SerialUSB.print("Exit status: "); + SerialUSB.println(client.exitValue()); + + if (client.exitValue() == 0) { + SerialUSB.print("Response Code: "); + SerialUSB.println(client.getResponseCode()); + + SerialUSB.println("Response Headers:"); + SerialUSB.println(client.getResponseHeaders()); + + SerialUSB.println("Response Body:"); + while (client.available() > 0) { + char c = client.read(); + SerialUSB.print(c); + } + } +} + +void loop() { +} diff --git a/examples/BasicAuth/BasicAuth.ino b/examples/BasicAuth/BasicAuth.ino new file mode 100644 index 0000000..b60e5c1 --- /dev/null +++ b/examples/BasicAuth/BasicAuth.ino @@ -0,0 +1,40 @@ + +#include +#include + +void setup() { + pinMode(13, OUTPUT); + digitalWrite(13, LOW); + Bridge.begin(); // Initialize Bridge + digitalWrite(13, HIGH); + + SerialUSB.begin(9600); + while (!SerialUSB); // wait for a serial connection + + BridgeHttpClient client; + client.basicAuth("user", "password"); + + client.enableInsecure(); // Using HTTPS and peer cert. will not be able to auth. + + client.get("https://httpbin.org/headers"); + + SerialUSB.print("Exit status: "); + SerialUSB.println(client.exitValue()); + + if (client.exitValue() == 0) { + SerialUSB.print("Response Code: "); + SerialUSB.println(client.getResponseCode()); + + SerialUSB.println("Response Headers:"); + SerialUSB.println(client.getResponseHeaders()); + + SerialUSB.println("Response Body:"); + while (client.available() > 0) { + char c = client.read(); + SerialUSB.print(c); + } + } +} + +void loop() { +} diff --git a/examples/PostJSON/PostJSON.ino b/examples/PostJSON/PostJSON.ino new file mode 100644 index 0000000..63909cc --- /dev/null +++ b/examples/PostJSON/PostJSON.ino @@ -0,0 +1,42 @@ + +#include +#include + +void setup() { + pinMode(13, OUTPUT); + digitalWrite(13, LOW); + Bridge.begin(); // Initialize Bridge + digitalWrite(13, HIGH); + + SerialUSB.begin(9600); + while (!SerialUSB); // wait for a serial connection + + BridgeHttpClient client; + client.addHeader("X-Api-Key: 12345"); + client.addHeader("Accept: application/json"); + client.addHeader("Content-Type: application/json"); + + client.enableInsecure(); // Using HTTPS and peer cert. will not be able to auth. + + client.post("https://httpbin.org/post", "{\"key\":\"value\"}"); + + SerialUSB.print("Exit status: "); + SerialUSB.println(client.exitValue()); + + if (client.exitValue() == 0) { + SerialUSB.print("Response Code: "); + SerialUSB.println(client.getResponseCode()); + + SerialUSB.println("Response Headers:"); + SerialUSB.println(client.getResponseHeaders()); + + SerialUSB.println("Response Body:"); + while (client.available() > 0) { + char c = client.read(); + SerialUSB.print(c); + } + } +} + +void loop() { +} diff --git a/examples/SimpleGet/SimpleGet.ino b/examples/SimpleGet/SimpleGet.ino new file mode 100644 index 0000000..5c63655 --- /dev/null +++ b/examples/SimpleGet/SimpleGet.ino @@ -0,0 +1,37 @@ + +#include +#include + +void setup() { + pinMode(13, OUTPUT); + digitalWrite(13, LOW); + Bridge.begin(); // Initialize Bridge + digitalWrite(13, HIGH); + + SerialUSB.begin(9600); + while (!SerialUSB); // wait for a serial connection + + BridgeHttpClient client; + + client.get("http://httpbin.org/headers"); + + SerialUSB.print("Exit status: "); + SerialUSB.println(client.exitValue()); + + if (client.exitValue() == 0) { + SerialUSB.print("Response Code: "); + SerialUSB.println(client.getResponseCode()); + + SerialUSB.println("Response Headers:"); + SerialUSB.println(client.getResponseHeaders()); + + SerialUSB.println("Response Body:"); + while (client.available() > 0) { + char c = client.read(); + SerialUSB.print(c); + } + } +} + +void loop() { +} diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..7745737 --- /dev/null +++ b/keywords.txt @@ -0,0 +1,32 @@ +####################################### +# Syntax Coloring Map +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### +BridgeHttpClient KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +get KEYWORD2 +post KEYWORD2 +put KEYWORD2 +del KEYWORD2 +getResponseCode KEYWORD2 +getResponseHeaders KEYWORD2 +getAsync KEYWORD2 +postAsync KEYWORD2 +putAsync KEYWORD2 +delAsync KEYWORD2 +enableInsecure KEYWORD2 +addHeader KEYWORD2 +basicAuth KEYWORD2 +clearHeaders KEYWORD2 +clearAuth KEYWORD2 +getResponseHeaderValue KEYWORD2 + +###################################### +# Constants (LITERAL1) +####################################### diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..a8a5691 --- /dev/null +++ b/library.properties @@ -0,0 +1,9 @@ +name=BridgeHttpClient +version=1.0.0 +author=Imre Horvath +maintainer=Imre Horvath +sentence=A practical and easy to use generic HTTP client library for the Yun. +paragraph=GET/POST/PUT/DELETE, sync/async requests, request/response headers, basic auth and more. +category=Communication +url=https://github.com/imrehorvath/BridgeHttpClient +architectures=*