From 0a613cdae520c6dc7bcc57ba0f336c907a0ea2bd Mon Sep 17 00:00:00 2001 From: Ed Coyne Date: Tue, 12 Jan 2016 14:21:01 -0800 Subject: [PATCH 01/15] Refactor Firebase module, split into more focues subclasses, change return type to include status, changed style to match Google Style guide. --- Firebase.cpp | 178 +++++++++++------- Firebase.h | 108 ++++++++--- .../FirebasePush_ESP8266.ino | 20 +- .../FirebaseStream_ESP8266.ino | 18 +- 4 files changed, 208 insertions(+), 116 deletions(-) diff --git a/Firebase.cpp b/Firebase.cpp index 63d3188e..ac94133c 100644 --- a/Firebase.cpp +++ b/Firebase.cpp @@ -15,114 +15,152 @@ // #include "Firebase.h" -const char* firebaseFingerprint = "7A 54 06 9B DC 7A 25 B3 86 8D 66 53 48 2C 0B 96 42 C7 B3 0A"; -const uint16_t firebasePort = 443; +namespace { +const char* kFirebaseFingerprint = "7A 54 06 9B DC 7A 25 B3 86 8D 66 53 48 2C 0B 96 42 C7 B3 0A"; +const uint16_t kFirebasePort = 443; +} // namespace -Firebase::Firebase(const String& host) : _host(host) { - _http.setReuse(true); +FirebaseResult::FirebaseResult(int status) : status_(status) { } -Firebase& Firebase::auth(const String& auth) { - _auth = auth; - return *this; +bool FirebaseResult::isOk() const { + return status_ == HTTP_CODE_OK; } -String Firebase::get(const String& path) { - return sendRequestGetBody("GET", path); +bool FirebaseResult::isError() const { + return status_ < 0; } -String Firebase::push(const String& path, const String& value) { - return sendRequestGetBody("POST", path, value); +String FirebaseResult::errorMessage() const { + return HTTPClient::errorToString(status_); } -bool Firebase::remove(const String& path) { - int status = sendRequest("DELETE", path); - return status == HTTP_CODE_OK; +FirebaseResultWithMessage::FirebaseResultWithMessage(const FirebaseResult& result, + const String& message) + : FirebaseResultWithMessage(result.httpStatus(), message) { } -Firebase& Firebase::stream(const String& path) { - _error.reset(); - String url = makeURL(path); - const char* headers[] = {"Location"}; - _http.setReuse(true); - _http.begin(_host.c_str(), firebasePort, url.c_str(), true, firebaseFingerprint); - _http.collectHeaders(headers, 1); - _http.addHeader("Accept", "text/event-stream"); - int statusCode = _http.sendRequest("GET", (uint8_t*)NULL, 0); - String location; - // TODO(proppy): Add a max redirect check - while (statusCode == 307) { - location = _http.header("Location"); - _http.setReuse(false); - _http.end(); - _http.setReuse(true); - _http.begin(location, firebaseFingerprint); - statusCode = _http.sendRequest("GET", (uint8_t*)NULL, 0); - } - if (statusCode != 200) { - _error.set(statusCode, - "stream " + location + ": " - + HTTPClient::errorToString(statusCode)); - } +FirebaseResultWithMessage::FirebaseResultWithMessage(int status, + const String& message) + : FirebaseResult(status) { + message_ = message; +} + +const String& FirebaseResultWithMessage::message() const { + return message_; +} + +FirebaseConnection::FirebaseConnection(const String& host) : host_(host) { + http_.setReuse(true); +} + +FirebaseConnection& FirebaseConnection::auth(const String& auth) { + auth_ = auth; return *this; } -String Firebase::makeURL(const String& path) { +FirebaseResult FirebaseConnection::sendRequest(const char* method, const String& path) { + return sendRequest(method, path, ""); +} + +FirebaseResult FirebaseConnection::sendRequest(const char* method, const String& path, const String& value) { + const String url = makeURL(path); + http_.begin(host_.c_str(), kFirebasePort, url.c_str(), true, kFirebaseFingerprint); + int statusCode = http_.sendRequest(method, (uint8_t*)value.c_str(), value.length()); + return FirebaseResult(statusCode); +} + +FirebaseResultWithMessage FirebaseConnection::sendRequestGetBody(const char* method, const String& path) { + return sendRequestGetBody(method, path, ""); +} + +FirebaseResultWithMessage FirebaseConnection::sendRequestGetBody(const char* method, const String& path, const String& value) { + FirebaseResult result = sendRequest(method, path, value); + return FirebaseResultWithMessage(result, http_.getString()); +} + +String FirebaseConnection::makeURL(const String& path) { String url; if (path[0] != '/') { url = "/"; } url += path + ".json"; - if (_auth.length() > 0) { - url += "?auth=" + _auth; + if (auth_.length() > 0) { + url += "?auth=" + auth_; } return url; } -int Firebase::sendRequest(const char* method, const String& path, const String& value) { - String url = makeURL(path); - _http.begin(_host.c_str(), firebasePort, url.c_str(), true, firebaseFingerprint); - int statusCode = _http.sendRequest(method, (uint8_t*)value.c_str(), value.length()); - setError(method, url, statusCode); - return statusCode; +Firebase::Firebase(const String& host) : connection_(host) { } -String Firebase::sendRequestGetBody(const char* method, const String& path, const String& value) { - sendRequest(method, path, value); - if (_error.code() != 0) { - return ""; - } - // no _http.end() because of connection reuse. - return _http.getString(); +Firebase& Firebase::auth(const String& auth) { + connection_.auth(auth); + return *this; +} + +FirebaseResultWithMessage Firebase::get(const String& path) { + return connection_.sendRequestGetBody("GET", path); +} + +FirebaseResultWithMessage Firebase::push(const String& path, const String& value) { + return connection_.sendRequestGetBody("POST", path, value); +} + +FirebaseResult Firebase::remove(const String& path) { + return connection_.sendRequest("DELETE", path); } -void Firebase::setError(const char* method, const String& url, int statusCode) { - _error.reset(); - if (statusCode < 0) { - _error.set(statusCode, - String(method) + " " + url + ": " - + HTTPClient::errorToString(statusCode)); +FirebaseEventStream::FirebaseEventStream(const String& host) : connection_(host) { +} + +FirebaseEventStream& FirebaseEventStream::auth(const String& auth) { + connection_.auth(auth); + return *this; +} + +FirebaseResult FirebaseEventStream::connect(const String& path) { + String url = connection_.makeURL(path); + auto& http = connection_.httpClient(); + http.setReuse(true); + http.begin(connection_.host().c_str(), kFirebasePort, url.c_str(), true, + kFirebaseFingerprint); + const char* headers[] = {"Location"}; + http.collectHeaders(headers, 1); + http.addHeader("Accept", "text/event-stream"); + int statusCode = http.sendRequest("GET", (uint8_t*)NULL, 0); + + String location; + // TODO(proppy): Add a max redirect check + while (statusCode == HTTP_CODE_TEMPORARY_REDIRECT) { + location = http.header("Location"); + http.setReuse(false); + http.end(); + http.setReuse(true); + http.begin(location, kFirebaseFingerprint); + statusCode = http.sendRequest("GET", (uint8_t*)NULL, 0); } + return FirebaseResult(statusCode); } -bool Firebase::connected() { - return _http.connected(); +bool FirebaseEventStream::connected() { + return connection_.httpClient().connected(); } -bool Firebase::available() { - return _http.getStreamPtr()->available(); +bool FirebaseEventStream::available() { + return connection_.httpClient().getStreamPtr()->available(); } -Firebase::Event Firebase::read(String& event) { - auto client = _http.getStreamPtr(); - Event type;; +FirebaseEventStream::Event FirebaseEventStream::read(String& event) { + auto client = connection_.httpClient().getStreamPtr(); + Event type; String typeStr = client->readStringUntil('\n').substring(7); if (typeStr == "put") { - type = Firebase::Event::PUT; + type = FirebaseEventStream::Event::PUT; } else if (typeStr == "patch") { - type = Firebase::Event::PATCH; + type = FirebaseEventStream::Event::PATCH; } else { - type = Firebase::Event::UNKNOWN; + type = FirebaseEventStream::Event::UNKNOWN; } event = client->readStringUntil('\n').substring(6); client->readStringUntil('\n'); // consume separator diff --git a/Firebase.h b/Firebase.h index 3b2a01e4..073558e7 100644 --- a/Firebase.h +++ b/Firebase.h @@ -25,21 +25,59 @@ #include #include -// FirebaseError represents a Firebase API error with a code and a -// message. -class FirebaseError { +class FirebaseResult { public: - operator bool() const { return _code < 0; } - int code() const { return _code; } - const String& message() const { return _message; } - void reset() { set(0, ""); } - void set(int code, const String& message) { - _code = code; - _message = message; + FirebaseResult(int status); + bool isError() const; + bool isOk() const; + String errorMessage() const; + + int httpStatus() const { + return status_; } + + protected: + int status_; +}; + +class FirebaseResultWithMessage : public FirebaseResult { + public: + FirebaseResultWithMessage(int status, const String& message); + FirebaseResultWithMessage(const FirebaseResult& result, + const String& message); + + // Check isError() before calling this. + const String& message() const; + + private: + String message_ ; +}; + +class FirebaseConnection { + public: + FirebaseConnection(const String& host); + FirebaseConnection& auth(const String& auth); + + const String& host() { + return host_; + } + + HTTPClient& httpClient(){ + return http_; + } + + String makeURL(const String& path); + + FirebaseResult sendRequest(const char* method, const String& path, const String& value); + FirebaseResult sendRequest(const char* method, const String& path); + + FirebaseResultWithMessage sendRequestGetBody(const char* method, const String& path); + FirebaseResultWithMessage sendRequestGetBody(const char* method, const String& path, const String& value); + private: - int _code = 0; - String _message = ""; + HTTPClient http_; + const String host_; + String auth_; }; // Firebase is the connection to firebase. @@ -47,31 +85,41 @@ class Firebase { public: Firebase(const String& host); Firebase& auth(const String& auth); - const FirebaseError& error() const { - return _error; - } - String get(const String& path); - String push(const String& path, const String& value); - bool remove(const String& path); - bool connected(); - Firebase& stream(const String& path); - bool available(); + + // Fetch result at "path" to a local variable. If the value is too large you will exceed + // local memory. + FirebaseResultWithMessage get(const String& path); + + // Add new value to list at "path", will return child name of new item. + FirebaseResultWithMessage push(const String& path, const String& value); + + // Deletes value at "path" from server. + FirebaseResult remove(const String& path); + + private: + FirebaseConnection connection_; +}; + +// Listens on a stream of events from Firebase backend. +class FirebaseEventStream { + public: enum Event { UNKNOWN, PUT, PATCH }; + + FirebaseEventStream(const String& host); + FirebaseEventStream& auth(const String& auth); + + FirebaseResult connect(const String& path); Event read(String& event); + + bool connected(); + bool available(); private: - String makeURL(const String& path); - int sendRequest(const char* method, const String& path, const String& value = ""); - String sendRequestGetBody(const char* method, const String& path, const String& value = ""); - void setError(const char* method, const String& url, int status_code); - - HTTPClient _http; - String _host; - String _auth; - FirebaseError _error; + FirebaseConnection connection_; }; + #endif // firebase_h diff --git a/examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino b/examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino index d04b6fed..526c9c2f 100644 --- a/examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino +++ b/examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino @@ -37,19 +37,23 @@ void setup() { Serial.print("connected: "); Serial.println(WiFi.localIP()); - // add a new entry. - String l = fbase.push("/logs", "{\".sv\": \"timestamp\"}"); - // handle error. - if (fbase.error()) { + // add a new entry. + FirebaseResultWithMessage push = fbase.push("/logs", "{\".sv\": \"timestamp\"}"); + if (push.isError()) { Serial.println("Firebase request failed"); - Serial.println(fbase.error().message()); + Serial.println(push.errorMessage()); return; } + // print response. - Serial.println(l); + Serial.println(push.message()); + // print all entries. - Serial.println(fbase.get("/logs")); + FirebaseResultWithMessage fetch = fbase.get("/logs"); + if (!fetch.isError()) { + Serial.println(fetch.message()); + } } void loop() { -} \ No newline at end of file +} diff --git a/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino b/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino index 13bb2174..7382911d 100644 --- a/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino +++ b/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino @@ -24,7 +24,8 @@ #define OLED_RESET 10 Adafruit_SSD1306 display(OLED_RESET); -Firebase fbase = Firebase("publicdata-cryptocurrency.firebaseio.com"); +FirebaseEventStream fbase = FirebaseEventStream("publicdata-cryptocurrency.firebaseio.com"); +FirebaseResult connectionResult; void setup() { Serial.begin(9600); @@ -43,21 +44,22 @@ void setup() { Serial.print("connected: "); Serial.println(WiFi.localIP()); - fbase.stream("/bitcoin"); + connectionResult = fbase.connect("/bitcoin"); } -void loop() { - if (fbase.error()) { +void loop() { + if (connectionResult.isError()) { Serial.println("streaming error"); - Serial.println(fbase.error().message()); + Serial.println(connectionResult.errorMessage()); } + if (fbase.available()) { String event; auto type = fbase.read(event); Serial.print("event: "); Serial.println(type); - if (type != Firebase::Event::UNKNOWN) { + if (type != FirebaseEventStream::Event::UNKNOWN) { Serial.print("data: "); Serial.println(event); @@ -69,5 +71,5 @@ void loop() { display.println(event); display.display(); } - } -} \ No newline at end of file + } +} From d805dc63d7dfcece40d415fe7bd2a6558e8b64f7 Mon Sep 17 00:00:00 2001 From: Ed Coyne Date: Tue, 12 Jan 2016 14:43:01 -0800 Subject: [PATCH 02/15] removed FirebaseResultWithMessage consolidated into one for simplicity --- Firebase.cpp | 150 ++++++++++++++++++++++++++------------------------- Firebase.h | 26 ++++----- 2 files changed, 86 insertions(+), 90 deletions(-) diff --git a/Firebase.cpp b/Firebase.cpp index ac94133c..b09490c3 100644 --- a/Firebase.cpp +++ b/Firebase.cpp @@ -20,77 +20,6 @@ const char* kFirebaseFingerprint = "7A 54 06 9B DC 7A 25 B3 86 8D 66 53 48 2C 0B const uint16_t kFirebasePort = 443; } // namespace -FirebaseResult::FirebaseResult(int status) : status_(status) { -} - -bool FirebaseResult::isOk() const { - return status_ == HTTP_CODE_OK; -} - -bool FirebaseResult::isError() const { - return status_ < 0; -} - -String FirebaseResult::errorMessage() const { - return HTTPClient::errorToString(status_); -} - -FirebaseResultWithMessage::FirebaseResultWithMessage(const FirebaseResult& result, - const String& message) - : FirebaseResultWithMessage(result.httpStatus(), message) { -} - -FirebaseResultWithMessage::FirebaseResultWithMessage(int status, - const String& message) - : FirebaseResult(status) { - message_ = message; -} - -const String& FirebaseResultWithMessage::message() const { - return message_; -} - -FirebaseConnection::FirebaseConnection(const String& host) : host_(host) { - http_.setReuse(true); -} - -FirebaseConnection& FirebaseConnection::auth(const String& auth) { - auth_ = auth; - return *this; -} - -FirebaseResult FirebaseConnection::sendRequest(const char* method, const String& path) { - return sendRequest(method, path, ""); -} - -FirebaseResult FirebaseConnection::sendRequest(const char* method, const String& path, const String& value) { - const String url = makeURL(path); - http_.begin(host_.c_str(), kFirebasePort, url.c_str(), true, kFirebaseFingerprint); - int statusCode = http_.sendRequest(method, (uint8_t*)value.c_str(), value.length()); - return FirebaseResult(statusCode); -} - -FirebaseResultWithMessage FirebaseConnection::sendRequestGetBody(const char* method, const String& path) { - return sendRequestGetBody(method, path, ""); -} - -FirebaseResultWithMessage FirebaseConnection::sendRequestGetBody(const char* method, const String& path, const String& value) { - FirebaseResult result = sendRequest(method, path, value); - return FirebaseResultWithMessage(result, http_.getString()); -} - -String FirebaseConnection::makeURL(const String& path) { - String url; - if (path[0] != '/') { - url = "/"; - } - url += path + ".json"; - if (auth_.length() > 0) { - url += "?auth=" + auth_; - } - return url; -} - Firebase::Firebase(const String& host) : connection_(host) { } @@ -99,11 +28,11 @@ Firebase& Firebase::auth(const String& auth) { return *this; } -FirebaseResultWithMessage Firebase::get(const String& path) { +FirebaseResult Firebase::get(const String& path) { return connection_.sendRequestGetBody("GET", path); } -FirebaseResultWithMessage Firebase::push(const String& path, const String& value) { +FirebaseResult Firebase::push(const String& path, const String& value) { return connection_.sendRequestGetBody("POST", path, value); } @@ -111,6 +40,8 @@ FirebaseResult Firebase::remove(const String& path) { return connection_.sendRequest("DELETE", path); } +/* FirebaseEventStream */ + FirebaseEventStream::FirebaseEventStream(const String& host) : connection_(host) { } @@ -166,3 +97,76 @@ FirebaseEventStream::Event FirebaseEventStream::read(String& event) { client->readStringUntil('\n'); // consume separator return type; } + +/* FirebaseConnection */ + +FirebaseConnection::FirebaseConnection(const String& host) : host_(host) { + http_.setReuse(true); +} + +FirebaseConnection& FirebaseConnection::auth(const String& auth) { + auth_ = auth; + return *this; +} + +FirebaseResult FirebaseConnection::sendRequest(const char* method, const String& path) { + return sendRequest(method, path, ""); +} + +FirebaseResult FirebaseConnection::sendRequest(const char* method, const String& path, const String& value) { + const String url = makeURL(path); + http_.begin(host_.c_str(), kFirebasePort, url.c_str(), true, kFirebaseFingerprint); + int statusCode = http_.sendRequest(method, (uint8_t*)value.c_str(), value.length()); + return FirebaseResult(statusCode); +} + +FirebaseResult FirebaseConnection::sendRequestGetBody(const char* method, const String& path) { + return sendRequestGetBody(method, path, ""); +} + +FirebaseResult FirebaseConnection::sendRequestGetBody(const char* method, const String& path, const String& value) { + FirebaseResult result = sendRequest(method, path, value); + return FirebaseResult(result.httpStatus(), http_.getString()); +} + +String FirebaseConnection::makeURL(const String& path) { + String url; + if (path[0] != '/') { + url = "/"; + } + url += path + ".json"; + if (auth_.length() > 0) { + url += "?auth=" + auth_; + } + return url; +} + +/* FirebaseResult */ + +FirebaseResult::FirebaseResult(int status) : status_(status) { +} + +FirebaseResult::FirebaseResult(int status, const String& response) + : status_(status), response_(response) { +} + +FirebaseResult::FirebaseResult(const FirebaseResult& other) { + status_ = other.status_; + response_ = other.response_; +} + +bool FirebaseResult::isOk() const { + return status_ == HTTP_CODE_OK; +} + +bool FirebaseResult::isError() const { + return status_ < 0; +} + +String FirebaseResult::errorMessage() const { + return HTTPClient::errorToString(status_); +} + +const String& FirebaseResult::response() const { + return response_; +} diff --git a/Firebase.h b/Firebase.h index 073558e7..a091fb3b 100644 --- a/Firebase.h +++ b/Firebase.h @@ -28,9 +28,13 @@ class FirebaseResult { public: FirebaseResult(int status); + FirebaseResult(int status, const String& response); + FirebaseResult(const FirebaseResult& result); + bool isError() const; bool isOk() const; String errorMessage() const; + const String& response() const; int httpStatus() const { return status_; @@ -38,19 +42,7 @@ class FirebaseResult { protected: int status_; -}; - -class FirebaseResultWithMessage : public FirebaseResult { - public: - FirebaseResultWithMessage(int status, const String& message); - FirebaseResultWithMessage(const FirebaseResult& result, - const String& message); - - // Check isError() before calling this. - const String& message() const; - - private: - String message_ ; + String response_; }; class FirebaseConnection { @@ -71,8 +63,8 @@ class FirebaseConnection { FirebaseResult sendRequest(const char* method, const String& path, const String& value); FirebaseResult sendRequest(const char* method, const String& path); - FirebaseResultWithMessage sendRequestGetBody(const char* method, const String& path); - FirebaseResultWithMessage sendRequestGetBody(const char* method, const String& path, const String& value); + FirebaseResult sendRequestGetBody(const char* method, const String& path); + FirebaseResult sendRequestGetBody(const char* method, const String& path, const String& value); private: HTTPClient http_; @@ -88,10 +80,10 @@ class Firebase { // Fetch result at "path" to a local variable. If the value is too large you will exceed // local memory. - FirebaseResultWithMessage get(const String& path); + FirebaseResult get(const String& path); // Add new value to list at "path", will return child name of new item. - FirebaseResultWithMessage push(const String& path, const String& value); + FirebaseResult push(const String& path, const String& value); // Deletes value at "path" from server. FirebaseResult remove(const String& path); From 217d58faedcbbc4411027dbaf2686497bc822817 Mon Sep 17 00:00:00 2001 From: Ed Coyne Date: Tue, 12 Jan 2016 14:51:27 -0800 Subject: [PATCH 03/15] Added comments for clarify --- Firebase.h | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/Firebase.h b/Firebase.h index a091fb3b..0eaa34d2 100644 --- a/Firebase.h +++ b/Firebase.h @@ -25,26 +25,36 @@ #include #include +//TODO(edcoyne) split these into multiple files. + +// Result from call to Firebase backend. ALWAYS check isError() before +// expecting any data. class FirebaseResult { public: FirebaseResult(int status); FirebaseResult(int status, const String& response); FirebaseResult(const FirebaseResult& result); + // True if there was an error completeing call. bool isError() const; - bool isOk() const; String errorMessage() const; + + // True if http status code is 200(OK). + bool isOk() const; + // Message sent back from Firebase backend. const String& response() const; int httpStatus() const { return status_; } - protected: + private: int status_; String response_; }; +// Low level connection to Firebase backend, you probably want the +// Firebase class below. class FirebaseConnection { public: FirebaseConnection(const String& host); @@ -72,7 +82,7 @@ class FirebaseConnection { String auth_; }; -// Firebase is the connection to firebase. +// Primary client to the Firebase backend. class Firebase { public: Firebase(const String& host); @@ -104,14 +114,19 @@ class FirebaseEventStream { FirebaseEventStream(const String& host); FirebaseEventStream& auth(const String& auth); + // Connect to backend and start receiving events. FirebaseResult connect(const String& path); + // Read next event in stream. Event read(String& event); + // True if connected to backend. bool connected(); + + // True if there is an event available. bool available(); + private: FirebaseConnection connection_; }; - #endif // firebase_h From b2438cf6393fc212d68a5d0d8f66f19d6b53c335 Mon Sep 17 00:00:00 2001 From: Ed Coyne Date: Wed, 13 Jan 2016 13:57:22 -0800 Subject: [PATCH 04/15] removed old reference to FirebaseResultWithMessage --- examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino b/examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino index 526c9c2f..51df5b4f 100644 --- a/examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino +++ b/examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino @@ -38,7 +38,7 @@ void setup() { Serial.println(WiFi.localIP()); // add a new entry. - FirebaseResultWithMessage push = fbase.push("/logs", "{\".sv\": \"timestamp\"}"); + FirebaseResult push = fbase.push("/logs", "{\".sv\": \"timestamp\"}"); if (push.isError()) { Serial.println("Firebase request failed"); Serial.println(push.errorMessage()); @@ -49,7 +49,7 @@ void setup() { Serial.println(push.message()); // print all entries. - FirebaseResultWithMessage fetch = fbase.get("/logs"); + FirebaseResult fetch = fbase.get("/logs"); if (!fetch.isError()) { Serial.println(fetch.message()); } From d5dd303b484088595156f8568144fe1c2eb7f7cf Mon Sep 17 00:00:00 2001 From: Ed Coyne Date: Wed, 13 Jan 2016 17:18:59 -0800 Subject: [PATCH 05/15] Refactor out FirebaseCall --- Firebase.cpp | 155 ++++++++---------- Firebase.h | 103 +++++------- .../FirebasePush_ESP8266.ino | 8 +- .../FirebaseStream_ESP8266.ino | 14 +- 4 files changed, 121 insertions(+), 159 deletions(-) diff --git a/Firebase.cpp b/Firebase.cpp index b09490c3..398676d2 100644 --- a/Firebase.cpp +++ b/Firebase.cpp @@ -18,72 +18,81 @@ namespace { const char* kFirebaseFingerprint = "7A 54 06 9B DC 7A 25 B3 86 8D 66 53 48 2C 0B 96 42 C7 B3 0A"; const uint16_t kFirebasePort = 443; + +String makeUrl(const String& path, const String& auth) { + String url; + if (path[0] != '/') { + url = "/"; + } + url += path + ".json"; + if (auth.length() > 0) { + url += "?auth=" + auth; + } + return url; +} + } // namespace -Firebase::Firebase(const String& host) : connection_(host) { +Firebase::Firebase(const String& host) : host_(host) { + http_.setReuse(true); } Firebase& Firebase::auth(const String& auth) { - connection_.auth(auth); + auth_ = auth; return *this; } -FirebaseResult Firebase::get(const String& path) { - return connection_.sendRequestGetBody("GET", path); +FirebaseCall Firebase::get(const String& path) { + return FirebaseCall(host_, auth_, "GET", path, &http_); } -FirebaseResult Firebase::push(const String& path, const String& value) { - return connection_.sendRequestGetBody("POST", path, value); +FirebaseCall Firebase::push(const String& path, const String& value) { + return FirebaseCall(host_, auth_, "POST", path, value, &http_); } -FirebaseResult Firebase::remove(const String& path) { - return connection_.sendRequest("DELETE", path); +FirebaseCall Firebase::remove(const String& path) { + return FirebaseCall(host_, auth_, "DELETE", path, &http_); } -/* FirebaseEventStream */ - -FirebaseEventStream::FirebaseEventStream(const String& host) : connection_(host) { +FirebaseEventStream Firebase::stream(const String& path) { + return FirebaseEventStream(host_, auth_, path); } -FirebaseEventStream& FirebaseEventStream::auth(const String& auth) { - connection_.auth(auth); - return *this; -} +/* FirebaseEventStream */ -FirebaseResult FirebaseEventStream::connect(const String& path) { - String url = connection_.makeURL(path); - auto& http = connection_.httpClient(); - http.setReuse(true); - http.begin(connection_.host().c_str(), kFirebasePort, url.c_str(), true, - kFirebaseFingerprint); +FirebaseEventStream::FirebaseEventStream(const String& host, const String& auth, + const String& path) { + const String url = makeUrl(path, auth); + http_.setReuse(true); + http_.begin(host.c_str(), kFirebasePort, url.c_str(), true, + kFirebaseFingerprint); const char* headers[] = {"Location"}; - http.collectHeaders(headers, 1); - http.addHeader("Accept", "text/event-stream"); - int statusCode = http.sendRequest("GET", (uint8_t*)NULL, 0); + http_.collectHeaders(headers, 1); + http_.addHeader("Accept", "text/event-stream"); + status_ = http_.sendRequest("GET", (uint8_t*)NULL, 0); String location; // TODO(proppy): Add a max redirect check - while (statusCode == HTTP_CODE_TEMPORARY_REDIRECT) { - location = http.header("Location"); - http.setReuse(false); - http.end(); - http.setReuse(true); - http.begin(location, kFirebaseFingerprint); - statusCode = http.sendRequest("GET", (uint8_t*)NULL, 0); + while (status_ == HTTP_CODE_TEMPORARY_REDIRECT) { + location = http_.header("Location"); + http_.setReuse(false); + http_.end(); + http_.setReuse(true); + http_.begin(location, kFirebaseFingerprint); + status_ = http_.sendRequest("GET", (uint8_t*)NULL, 0); } - return FirebaseResult(statusCode); } bool FirebaseEventStream::connected() { - return connection_.httpClient().connected(); + return http_.connected(); } bool FirebaseEventStream::available() { - return connection_.httpClient().getStreamPtr()->available(); + return http_.getStreamPtr()->available(); } FirebaseEventStream::Event FirebaseEventStream::read(String& event) { - auto client = connection_.httpClient().getStreamPtr(); + auto client = http_.getStreamPtr(); Event type; String typeStr = client->readStringUntil('\n').substring(7); if (typeStr == "put") { @@ -98,75 +107,43 @@ FirebaseEventStream::Event FirebaseEventStream::read(String& event) { return type; } -/* FirebaseConnection */ - -FirebaseConnection::FirebaseConnection(const String& host) : host_(host) { - http_.setReuse(true); -} - -FirebaseConnection& FirebaseConnection::auth(const String& auth) { - auth_ = auth; - return *this; -} - -FirebaseResult FirebaseConnection::sendRequest(const char* method, const String& path) { - return sendRequest(method, path, ""); -} - -FirebaseResult FirebaseConnection::sendRequest(const char* method, const String& path, const String& value) { - const String url = makeURL(path); - http_.begin(host_.c_str(), kFirebasePort, url.c_str(), true, kFirebaseFingerprint); - int statusCode = http_.sendRequest(method, (uint8_t*)value.c_str(), value.length()); - return FirebaseResult(statusCode); -} - -FirebaseResult FirebaseConnection::sendRequestGetBody(const char* method, const String& path) { - return sendRequestGetBody(method, path, ""); +bool FirebaseEventStream::isError() const { + return status_ < 0; } -FirebaseResult FirebaseConnection::sendRequestGetBody(const char* method, const String& path, const String& value) { - FirebaseResult result = sendRequest(method, path, value); - return FirebaseResult(result.httpStatus(), http_.getString()); +String FirebaseEventStream::errorMessage() const { + return error_message_; } -String FirebaseConnection::makeURL(const String& path) { - String url; - if (path[0] != '/') { - url = "/"; +/* FirebaseCall */ +FirebaseCall::FirebaseCall(const String& host, const String& auth, + const char* method, const String& path, const String& value, + HTTPClient* http) : http_(http) { + const String url = makeUrl(path, auth); + http_->begin(host.c_str(), kFirebasePort, url.c_str(), true, kFirebaseFingerprint); + status_ = http_->sendRequest(method, (uint8_t*)value.c_str(), value.length()); + if (isError()) { + error_message_ = String(method) + " " + url + ": " + HTTPClient::errorToString(status_); } - url += path + ".json"; - if (auth_.length() > 0) { - url += "?auth=" + auth_; - } - return url; -} - -/* FirebaseResult */ - -FirebaseResult::FirebaseResult(int status) : status_(status) { -} - -FirebaseResult::FirebaseResult(int status, const String& response) - : status_(status), response_(response) { } -FirebaseResult::FirebaseResult(const FirebaseResult& other) { - status_ = other.status_; - response_ = other.response_; +FirebaseCall::FirebaseCall(const String& host, const String& auth, + const char* method, const String& path, + HTTPClient* http) : FirebaseCall(host, auth, method, path, "", http) { } -bool FirebaseResult::isOk() const { +bool FirebaseCall::isOk() const { return status_ == HTTP_CODE_OK; } -bool FirebaseResult::isError() const { +bool FirebaseCall::isError() const { return status_ < 0; } -String FirebaseResult::errorMessage() const { - return HTTPClient::errorToString(status_); +String FirebaseCall::errorMessage() const { + return error_message_; } -const String& FirebaseResult::response() const { - return response_; +String FirebaseCall::rawResponse() { + return http_->getString(); } diff --git a/Firebase.h b/Firebase.h index 0eaa34d2..014e07b6 100644 --- a/Firebase.h +++ b/Firebase.h @@ -27,59 +27,67 @@ //TODO(edcoyne) split these into multiple files. -// Result from call to Firebase backend. ALWAYS check isError() before -// expecting any data. -class FirebaseResult { +class FirebaseCall { public: - FirebaseResult(int status); - FirebaseResult(int status, const String& response); - FirebaseResult(const FirebaseResult& result); + FirebaseCall(const String& host, const String& auth, + const char* method, const String& path, const String& value, + HTTPClient* http); + FirebaseCall(const String& host, const String& auth, + const char* method, const String& path, + HTTPClient* http); - // True if there was an error completeing call. + + // True if there was an error completing call. bool isError() const; String errorMessage() const; // True if http status code is 200(OK). bool isOk() const; - // Message sent back from Firebase backend. - const String& response() const; + + // Message sent back from Firebase backend. This pulls value to local memory, + // be careful if value can be large. + String rawResponse(); int httpStatus() const { return status_; } private: + FirebaseCall(HTTPClient* http); + + HTTPClient* http_; + int status_; - String response_; + String error_message_; }; -// Low level connection to Firebase backend, you probably want the -// Firebase class below. -class FirebaseConnection { +class FirebaseEventStream { public: - FirebaseConnection(const String& host); - FirebaseConnection& auth(const String& auth); + enum Event { + UNKNOWN, + PUT, + PATCH + }; - const String& host() { - return host_; - } + FirebaseEventStream(const String& host, const String& auth, const String& path); - HTTPClient& httpClient(){ - return http_; - } + // Read next event in stream. + Event read(String& event); - String makeURL(const String& path); + // True if connected to backend. + bool connected(); - FirebaseResult sendRequest(const char* method, const String& path, const String& value); - FirebaseResult sendRequest(const char* method, const String& path); + // True if there is an event available. + bool available(); - FirebaseResult sendRequestGetBody(const char* method, const String& path); - FirebaseResult sendRequestGetBody(const char* method, const String& path, const String& value); + // True if there was an error. + bool isError() const; + String errorMessage() const; private: HTTPClient http_; - const String host_; - String auth_; + int status_; + String error_message_; }; // Primary client to the Firebase backend. @@ -90,43 +98,20 @@ class Firebase { // Fetch result at "path" to a local variable. If the value is too large you will exceed // local memory. - FirebaseResult get(const String& path); + FirebaseCall get(const String& path); // Add new value to list at "path", will return child name of new item. - FirebaseResult push(const String& path, const String& value); + FirebaseCall push(const String& path, const String& value); // Deletes value at "path" from server. - FirebaseResult remove(const String& path); + FirebaseCall remove(const String& path); - private: - FirebaseConnection connection_; -}; - -// Listens on a stream of events from Firebase backend. -class FirebaseEventStream { - public: - enum Event { - UNKNOWN, - PUT, - PATCH - }; - - FirebaseEventStream(const String& host); - FirebaseEventStream& auth(const String& auth); - - // Connect to backend and start receiving events. - FirebaseResult connect(const String& path); - // Read next event in stream. - Event read(String& event); - - // True if connected to backend. - bool connected(); - - // True if there is an event available. - bool available(); + // Starts a stream of events that effect object at "path". + FirebaseEventStream stream(const String& path); private: - FirebaseConnection connection_; + HTTPClient http_; + String host_; + String auth_; }; - #endif // firebase_h diff --git a/examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino b/examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino index 51df5b4f..87e35a40 100644 --- a/examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino +++ b/examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino @@ -38,7 +38,7 @@ void setup() { Serial.println(WiFi.localIP()); // add a new entry. - FirebaseResult push = fbase.push("/logs", "{\".sv\": \"timestamp\"}"); + FirebaseCall push = fbase.push("/logs", "{\".sv\": \"timestamp\"}"); if (push.isError()) { Serial.println("Firebase request failed"); Serial.println(push.errorMessage()); @@ -46,12 +46,12 @@ void setup() { } // print response. - Serial.println(push.message()); + Serial.println(push.rawResponse()); // print all entries. - FirebaseResult fetch = fbase.get("/logs"); + FirebaseCall fetch = fbase.get("/logs"); if (!fetch.isError()) { - Serial.println(fetch.message()); + Serial.println(fetch.rawResponse()); } } diff --git a/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino b/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino index 7382911d..19b787b1 100644 --- a/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino +++ b/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino @@ -24,8 +24,8 @@ #define OLED_RESET 10 Adafruit_SSD1306 display(OLED_RESET); -FirebaseEventStream fbase = FirebaseEventStream("publicdata-cryptocurrency.firebaseio.com"); -FirebaseResult connectionResult; +Firebase fbase = Firebase("publicdata-cryptocurrency.firebaseio.com"); +FirebaseEventStream stream; void setup() { Serial.begin(9600); @@ -44,19 +44,19 @@ void setup() { Serial.print("connected: "); Serial.println(WiFi.localIP()); - connectionResult = fbase.connect("/bitcoin"); + stream = fbase.stream("/bitcoin"); } void loop() { - if (connectionResult.isError()) { + if (stream.isError()) { Serial.println("streaming error"); - Serial.println(connectionResult.errorMessage()); + Serial.println(stream.errorMessage()); } - if (fbase.available()) { + if (stream.available()) { String event; - auto type = fbase.read(event); + auto type = stream.read(event); Serial.print("event: "); Serial.println(type); if (type != FirebaseEventStream::Event::UNKNOWN) { From 23fbbed76e23bdb02ee543aa681caa6c55ee272f Mon Sep 17 00:00:00 2001 From: Ed Coyne Date: Wed, 13 Jan 2016 17:25:39 -0800 Subject: [PATCH 06/15] possibly fix streaming example --- examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino b/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino index 19b787b1..919902e9 100644 --- a/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino +++ b/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino @@ -25,7 +25,6 @@ Adafruit_SSD1306 display(OLED_RESET); Firebase fbase = Firebase("publicdata-cryptocurrency.firebaseio.com"); -FirebaseEventStream stream; void setup() { Serial.begin(9600); @@ -43,12 +42,11 @@ void setup() { Serial.println(); Serial.print("connected: "); Serial.println(WiFi.localIP()); - - stream = fbase.stream("/bitcoin"); } void loop() { + static FirebaseEventStream stream = fbase.stream("/bitcoin"); if (stream.isError()) { Serial.println("streaming error"); Serial.println(stream.errorMessage()); From 2bf3a4f1ab755a762b7a3879bd6c22b481b4270a Mon Sep 17 00:00:00 2001 From: Ed Coyne Date: Wed, 13 Jan 2016 17:33:10 -0800 Subject: [PATCH 07/15] Move helper classes below Firebase in Firebase.h --- Firebase.h | 52 ++++++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/Firebase.h b/Firebase.h index 014e07b6..958a21e8 100644 --- a/Firebase.h +++ b/Firebase.h @@ -27,6 +27,34 @@ //TODO(edcoyne) split these into multiple files. +class FirebaseCall; +class FirebaseEventStream; + +// Primary client to the Firebase backend. +class Firebase { + public: + Firebase(const String& host); + Firebase& auth(const String& auth); + + // Fetch result at "path" to a local variable. If the value is too large you will exceed + // local memory. + FirebaseCall get(const String& path); + + // Add new value to list at "path", will return child name of new item. + FirebaseCall push(const String& path, const String& value); + + // Deletes value at "path" from server. + FirebaseCall remove(const String& path); + + // Starts a stream of events that effect object at "path". + FirebaseEventStream stream(const String& path); + + private: + HTTPClient http_; + String host_; + String auth_; +}; + class FirebaseCall { public: FirebaseCall(const String& host, const String& auth, @@ -90,28 +118,4 @@ class FirebaseEventStream { String error_message_; }; -// Primary client to the Firebase backend. -class Firebase { - public: - Firebase(const String& host); - Firebase& auth(const String& auth); - - // Fetch result at "path" to a local variable. If the value is too large you will exceed - // local memory. - FirebaseCall get(const String& path); - - // Add new value to list at "path", will return child name of new item. - FirebaseCall push(const String& path, const String& value); - - // Deletes value at "path" from server. - FirebaseCall remove(const String& path); - - // Starts a stream of events that effect object at "path". - FirebaseEventStream stream(const String& path); - - private: - HTTPClient http_; - String host_; - String auth_; -}; #endif // firebase_h From 2df5bd65e694d8f3168b997ccc41c6a890957c6b Mon Sep 17 00:00:00 2001 From: Ed Coyne Date: Wed, 13 Jan 2016 17:39:36 -0800 Subject: [PATCH 08/15] Added error message back to streaming imp --- Firebase.cpp | 70 +++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/Firebase.cpp b/Firebase.cpp index 398676d2..cbfebb76 100644 --- a/Firebase.cpp +++ b/Firebase.cpp @@ -58,6 +58,40 @@ FirebaseEventStream Firebase::stream(const String& path) { return FirebaseEventStream(host_, auth_, path); } +/* FirebaseCall */ + +FirebaseCall::FirebaseCall(const String& host, const String& auth, + const char* method, const String& path, const String& value, + HTTPClient* http) : http_(http) { + const String url = makeUrl(path, auth); + http_->begin(host.c_str(), kFirebasePort, url.c_str(), true, kFirebaseFingerprint); + status_ = http_->sendRequest(method, (uint8_t*)value.c_str(), value.length()); + if (isError()) { + error_message_ = String(method) + " " + url + ": " + HTTPClient::errorToString(status_); + } +} + +FirebaseCall::FirebaseCall(const String& host, const String& auth, + const char* method, const String& path, + HTTPClient* http) : FirebaseCall(host, auth, method, path, "", http) { +} + +bool FirebaseCall::isOk() const { + return status_ == HTTP_CODE_OK; +} + +bool FirebaseCall::isError() const { + return status_ < 0; +} + +String FirebaseCall::errorMessage() const { + return error_message_; +} + +String FirebaseCall::rawResponse() { + return http_->getString(); +} + /* FirebaseEventStream */ FirebaseEventStream::FirebaseEventStream(const String& host, const String& auth, @@ -81,6 +115,11 @@ FirebaseEventStream::FirebaseEventStream(const String& host, const String& auth, http_.begin(location, kFirebaseFingerprint); status_ = http_.sendRequest("GET", (uint8_t*)NULL, 0); } + + if (status_ != 200) { + error_message_ = "stream " + location + ": " + + HTTPClient::errorToString(status_); + } } bool FirebaseEventStream::connected() { @@ -115,35 +154,4 @@ String FirebaseEventStream::errorMessage() const { return error_message_; } -/* FirebaseCall */ -FirebaseCall::FirebaseCall(const String& host, const String& auth, - const char* method, const String& path, const String& value, - HTTPClient* http) : http_(http) { - const String url = makeUrl(path, auth); - http_->begin(host.c_str(), kFirebasePort, url.c_str(), true, kFirebaseFingerprint); - status_ = http_->sendRequest(method, (uint8_t*)value.c_str(), value.length()); - if (isError()) { - error_message_ = String(method) + " " + url + ": " + HTTPClient::errorToString(status_); - } -} -FirebaseCall::FirebaseCall(const String& host, const String& auth, - const char* method, const String& path, - HTTPClient* http) : FirebaseCall(host, auth, method, path, "", http) { -} - -bool FirebaseCall::isOk() const { - return status_ == HTTP_CODE_OK; -} - -bool FirebaseCall::isError() const { - return status_ < 0; -} - -String FirebaseCall::errorMessage() const { - return error_message_; -} - -String FirebaseCall::rawResponse() { - return http_->getString(); -} From 077e060637b42c3c152b1b420bf0e692b74a568e Mon Sep 17 00:00:00 2001 From: Ed Coyne Date: Tue, 26 Jan 2016 15:54:02 -0800 Subject: [PATCH 09/15] Add call ids --- Firebase.cpp | 100 +++++++++++++++++++++++++++++++++++++++++---------- Firebase.h | 44 +++++++++++++++++------ 2 files changed, 114 insertions(+), 30 deletions(-) diff --git a/Firebase.cpp b/Firebase.cpp index cbfebb76..882f5707 100644 --- a/Firebase.cpp +++ b/Firebase.cpp @@ -33,48 +33,101 @@ String makeUrl(const String& path, const String& auth) { } // namespace -Firebase::Firebase(const String& host) : host_(host) { - http_.setReuse(true); +Firebase::Firebase(const String& host) { + connection_.reset(new FirebaseConnection(host)); } Firebase& Firebase::auth(const String& auth) { - auth_ = auth; + connection_->auth(auth); return *this; } FirebaseCall Firebase::get(const String& path) { - return FirebaseCall(host_, auth_, "GET", path, &http_); + return FirebaseCall("GET", path, ++current_call_id_, connection_.get()); } FirebaseCall Firebase::push(const String& path, const String& value) { - return FirebaseCall(host_, auth_, "POST", path, value, &http_); + return FirebaseCall("POST", path, value, ++current_call_id_, connection_.get()); } FirebaseCall Firebase::remove(const String& path) { - return FirebaseCall(host_, auth_, "DELETE", path, &http_); + return FirebaseCall("DELETE", path, ++current_call_id_, connection_.get()); } - +/* FirebaseEventStream Firebase::stream(const String& path) { return FirebaseEventStream(host_, auth_, path); +}*/ + +/* FirebaseConnection */ +FirebaseConnection::FirebaseConnection(const String& host) : host_(host) { + http_.setReuse(true); +} + +FirebaseConnection& FirebaseConnection::auth(const String& auth) { + auth_ = auth; + return *this; +} + +int FirebaseConnection::sendRequest(const char* method, const String& path, const String& value) { + const String url = makeUrl(path, auth_); + http_.begin(host_.c_str(), kFirebasePort, url.c_str(), true, kFirebaseFingerprint); + int status = http_.sendRequest(method, (uint8_t*)value.c_str(), value.length()); + if (status == HTTP_CODE_OK) { + remaining_call_buffer_ = http_.getSize(); + } + return status; +} + +String FirebaseConnection::getString() { + remaining_call_buffer_ = 0; + return http_.getString(); +} + +bool FirebaseConnection::isOwner(int call_id) { + return owning_call_id_ == call_id; +} + +void FirebaseConnection::setOwner(int call_id) { + owning_call_id_ = call_id; + drainResponseBuffer(); +} + +void FirebaseConnection::drainResponseBuffer() { + auto* stream = http_.getStreamPtr(); + Serial.println("Available "); + Serial.println(stream->available()); + + + const int buffer_size = 128; + uint8_t buffer[buffer_size]; + int read = 0; + int to_read = (buffer_size < remaining_call_buffer_) ? buffer_size : remaining_call_buffer_; + //TODO(edcoyne) This only reads what is available. Is this sufficient or should we wait? + while (remaining_call_buffer_ > 0 && (read = stream->read(buffer, to_read) > 0)) { + Serial.println("Draining "); + Serial.println(remaining_call_buffer_); + remaining_call_buffer_ -= read; + to_read = (buffer_size < remaining_call_buffer_) ? buffer_size : remaining_call_buffer_; + } + Serial.println("Done draining "); + Serial.println(remaining_call_buffer_); } /* FirebaseCall */ -FirebaseCall::FirebaseCall(const String& host, const String& auth, - const char* method, const String& path, const String& value, - HTTPClient* http) : http_(http) { - const String url = makeUrl(path, auth); - http_->begin(host.c_str(), kFirebasePort, url.c_str(), true, kFirebaseFingerprint); - status_ = http_->sendRequest(method, (uint8_t*)value.c_str(), value.length()); +FirebaseCall::FirebaseCall(const char* method, const String& path, const String& value, + int call_id, FirebaseConnection* connection) + : connection_(connection), call_id_(call_id) { + connection_->setOwner(call_id); + status_ = connection_->sendRequest(method, path, value); if (isError()) { - error_message_ = String(method) + " " + url + ": " + HTTPClient::errorToString(status_); + error_message_ = String(method) + " " + path + ": " + HTTPClient::errorToString(status_); } } -FirebaseCall::FirebaseCall(const String& host, const String& auth, - const char* method, const String& path, - HTTPClient* http) : FirebaseCall(host, auth, method, path, "", http) { -} +FirebaseCall::FirebaseCall(const char* method, const String& path, int call_id, + FirebaseConnection* connection) + : FirebaseCall(method, path, "", call_id, connection) {} bool FirebaseCall::isOk() const { return status_ == HTTP_CODE_OK; @@ -89,7 +142,16 @@ String FirebaseCall::errorMessage() const { } String FirebaseCall::rawResponse() { - return http_->getString(); + if (!connection_->isOwner(call_id_)) { + setErrorNotOwner(); + return ""; + } + return connection_->getString(); +} + +void FirebaseCall::setErrorNotOwner() { + status_ = kStatusNotConnectionOwner; + error_message_ = "Connection no longer owns connection"; } /* FirebaseEventStream */ diff --git a/Firebase.h b/Firebase.h index 958a21e8..f1e838c6 100644 --- a/Firebase.h +++ b/Firebase.h @@ -30,6 +30,27 @@ class FirebaseCall; class FirebaseEventStream; +class FirebaseConnection { + public: + FirebaseConnection(const String& host); + FirebaseConnection& auth(const String& auth); + + // Returns true if call with call_id owns the connection. + bool isOwner(int call_id); + void setOwner(int call_id); + + int sendRequest(const char* method, const String& path, const String& value); + String getString(); + private: + void drainResponseBuffer(); + + int owning_call_id_ = 0; + int remaining_call_buffer_ = 0; + HTTPClient http_; + String host_; + String auth_; +}; + // Primary client to the Firebase backend. class Firebase { public: @@ -49,21 +70,21 @@ class Firebase { // Starts a stream of events that effect object at "path". FirebaseEventStream stream(const String& path); + Firebase(const Firebase&) = delete; + Firebase& operator=(const Firebase&) = delete; private: - HTTPClient http_; - String host_; - String auth_; + int current_call_id_ = 0; + std::unique_ptr connection_; }; class FirebaseCall { public: - FirebaseCall(const String& host, const String& auth, - const char* method, const String& path, const String& value, - HTTPClient* http); - FirebaseCall(const String& host, const String& auth, - const char* method, const String& path, - HTTPClient* http); + static const int kStatusNotConnectionOwner = -1000; + FirebaseCall(const char* method, const String& path, const String& value, int call_id, + FirebaseConnection* connection); + FirebaseCall(const char* method, const String& path, int call_id, + FirebaseConnection* connection); // True if there was an error completing call. bool isError() const; @@ -81,10 +102,11 @@ class FirebaseCall { } private: - FirebaseCall(HTTPClient* http); + void setErrorNotOwner(); - HTTPClient* http_; + FirebaseConnection* connection_; + int call_id_; int status_; String error_message_; }; From 6f65250c73037a0e4cd20333e6f7f0ed2a9dd36d Mon Sep 17 00:00:00 2001 From: Ed Coyne Date: Wed, 27 Jan 2016 15:19:56 -0800 Subject: [PATCH 10/15] Revert "Add call ids" This reverts commit 077e060637b42c3c152b1b420bf0e692b74a568e. --- Firebase.cpp | 100 ++++++++++----------------------------------------- Firebase.h | 44 ++++++----------------- 2 files changed, 30 insertions(+), 114 deletions(-) diff --git a/Firebase.cpp b/Firebase.cpp index 882f5707..cbfebb76 100644 --- a/Firebase.cpp +++ b/Firebase.cpp @@ -33,101 +33,48 @@ String makeUrl(const String& path, const String& auth) { } // namespace -Firebase::Firebase(const String& host) { - connection_.reset(new FirebaseConnection(host)); +Firebase::Firebase(const String& host) : host_(host) { + http_.setReuse(true); } Firebase& Firebase::auth(const String& auth) { - connection_->auth(auth); + auth_ = auth; return *this; } FirebaseCall Firebase::get(const String& path) { - return FirebaseCall("GET", path, ++current_call_id_, connection_.get()); + return FirebaseCall(host_, auth_, "GET", path, &http_); } FirebaseCall Firebase::push(const String& path, const String& value) { - return FirebaseCall("POST", path, value, ++current_call_id_, connection_.get()); + return FirebaseCall(host_, auth_, "POST", path, value, &http_); } FirebaseCall Firebase::remove(const String& path) { - return FirebaseCall("DELETE", path, ++current_call_id_, connection_.get()); + return FirebaseCall(host_, auth_, "DELETE", path, &http_); } -/* + FirebaseEventStream Firebase::stream(const String& path) { return FirebaseEventStream(host_, auth_, path); -}*/ - -/* FirebaseConnection */ -FirebaseConnection::FirebaseConnection(const String& host) : host_(host) { - http_.setReuse(true); -} - -FirebaseConnection& FirebaseConnection::auth(const String& auth) { - auth_ = auth; - return *this; -} - -int FirebaseConnection::sendRequest(const char* method, const String& path, const String& value) { - const String url = makeUrl(path, auth_); - http_.begin(host_.c_str(), kFirebasePort, url.c_str(), true, kFirebaseFingerprint); - int status = http_.sendRequest(method, (uint8_t*)value.c_str(), value.length()); - if (status == HTTP_CODE_OK) { - remaining_call_buffer_ = http_.getSize(); - } - return status; -} - -String FirebaseConnection::getString() { - remaining_call_buffer_ = 0; - return http_.getString(); -} - -bool FirebaseConnection::isOwner(int call_id) { - return owning_call_id_ == call_id; -} - -void FirebaseConnection::setOwner(int call_id) { - owning_call_id_ = call_id; - drainResponseBuffer(); -} - -void FirebaseConnection::drainResponseBuffer() { - auto* stream = http_.getStreamPtr(); - Serial.println("Available "); - Serial.println(stream->available()); - - - const int buffer_size = 128; - uint8_t buffer[buffer_size]; - int read = 0; - int to_read = (buffer_size < remaining_call_buffer_) ? buffer_size : remaining_call_buffer_; - //TODO(edcoyne) This only reads what is available. Is this sufficient or should we wait? - while (remaining_call_buffer_ > 0 && (read = stream->read(buffer, to_read) > 0)) { - Serial.println("Draining "); - Serial.println(remaining_call_buffer_); - remaining_call_buffer_ -= read; - to_read = (buffer_size < remaining_call_buffer_) ? buffer_size : remaining_call_buffer_; - } - Serial.println("Done draining "); - Serial.println(remaining_call_buffer_); } /* FirebaseCall */ -FirebaseCall::FirebaseCall(const char* method, const String& path, const String& value, - int call_id, FirebaseConnection* connection) - : connection_(connection), call_id_(call_id) { - connection_->setOwner(call_id); - status_ = connection_->sendRequest(method, path, value); +FirebaseCall::FirebaseCall(const String& host, const String& auth, + const char* method, const String& path, const String& value, + HTTPClient* http) : http_(http) { + const String url = makeUrl(path, auth); + http_->begin(host.c_str(), kFirebasePort, url.c_str(), true, kFirebaseFingerprint); + status_ = http_->sendRequest(method, (uint8_t*)value.c_str(), value.length()); if (isError()) { - error_message_ = String(method) + " " + path + ": " + HTTPClient::errorToString(status_); + error_message_ = String(method) + " " + url + ": " + HTTPClient::errorToString(status_); } } -FirebaseCall::FirebaseCall(const char* method, const String& path, int call_id, - FirebaseConnection* connection) - : FirebaseCall(method, path, "", call_id, connection) {} +FirebaseCall::FirebaseCall(const String& host, const String& auth, + const char* method, const String& path, + HTTPClient* http) : FirebaseCall(host, auth, method, path, "", http) { +} bool FirebaseCall::isOk() const { return status_ == HTTP_CODE_OK; @@ -142,16 +89,7 @@ String FirebaseCall::errorMessage() const { } String FirebaseCall::rawResponse() { - if (!connection_->isOwner(call_id_)) { - setErrorNotOwner(); - return ""; - } - return connection_->getString(); -} - -void FirebaseCall::setErrorNotOwner() { - status_ = kStatusNotConnectionOwner; - error_message_ = "Connection no longer owns connection"; + return http_->getString(); } /* FirebaseEventStream */ diff --git a/Firebase.h b/Firebase.h index f1e838c6..958a21e8 100644 --- a/Firebase.h +++ b/Firebase.h @@ -30,27 +30,6 @@ class FirebaseCall; class FirebaseEventStream; -class FirebaseConnection { - public: - FirebaseConnection(const String& host); - FirebaseConnection& auth(const String& auth); - - // Returns true if call with call_id owns the connection. - bool isOwner(int call_id); - void setOwner(int call_id); - - int sendRequest(const char* method, const String& path, const String& value); - String getString(); - private: - void drainResponseBuffer(); - - int owning_call_id_ = 0; - int remaining_call_buffer_ = 0; - HTTPClient http_; - String host_; - String auth_; -}; - // Primary client to the Firebase backend. class Firebase { public: @@ -70,21 +49,21 @@ class Firebase { // Starts a stream of events that effect object at "path". FirebaseEventStream stream(const String& path); - Firebase(const Firebase&) = delete; - Firebase& operator=(const Firebase&) = delete; private: - int current_call_id_ = 0; - std::unique_ptr connection_; + HTTPClient http_; + String host_; + String auth_; }; class FirebaseCall { public: - static const int kStatusNotConnectionOwner = -1000; + FirebaseCall(const String& host, const String& auth, + const char* method, const String& path, const String& value, + HTTPClient* http); + FirebaseCall(const String& host, const String& auth, + const char* method, const String& path, + HTTPClient* http); - FirebaseCall(const char* method, const String& path, const String& value, int call_id, - FirebaseConnection* connection); - FirebaseCall(const char* method, const String& path, int call_id, - FirebaseConnection* connection); // True if there was an error completing call. bool isError() const; @@ -102,11 +81,10 @@ class FirebaseCall { } private: - void setErrorNotOwner(); + FirebaseCall(HTTPClient* http); - FirebaseConnection* connection_; + HTTPClient* http_; - int call_id_; int status_; String error_message_; }; From 33f81ef15f1914712078645eab56988085986790 Mon Sep 17 00:00:00 2001 From: Ed Coyne Date: Wed, 27 Jan 2016 16:29:14 -0800 Subject: [PATCH 11/15] Added independant opaque return types for all calls. --- Firebase.cpp | 112 ++++++++++++++++-- Firebase.h | 103 ++++++++++++---- .../FirebasePush_ESP8266.ino | 12 +- .../FirebaseStream_ESP8266.ino | 2 +- 4 files changed, 189 insertions(+), 40 deletions(-) diff --git a/Firebase.cpp b/Firebase.cpp index cbfebb76..9d86a712 100644 --- a/Firebase.cpp +++ b/Firebase.cpp @@ -31,6 +31,40 @@ String makeUrl(const String& path, const String& auth) { return url; } +class FirebaseCall { + public: + FirebaseCall(const String& host, const String& auth, + const char* method, const String& path, const String& value, + HTTPClient* http); + FirebaseCall(const String& host, const String& auth, + const char* method, const String& path, + HTTPClient* http); + + + // True if there was an error completing call. + bool isError() const; + String errorMessage() const; + + // True if http status code is 200(OK). + bool isOk() const; + + // Message sent back from Firebase backend. This pulls value to local memory, + // be careful if value can be large. + String rawResponse(); + + int httpStatus() const { + return status_; + } + + private: + FirebaseCall(HTTPClient* http); + + HTTPClient* http_; + + int status_; + String error_message_; +}; + } // namespace Firebase::Firebase(const String& host) : host_(host) { @@ -42,16 +76,29 @@ Firebase& Firebase::auth(const String& auth) { return *this; } -FirebaseCall Firebase::get(const String& path) { - return FirebaseCall(host_, auth_, "GET", path, &http_); +FirebaseGetResult Firebase::get(const String& path) { + FirebaseCall call(host_, auth_, "GET", path, &http_); + return call.isError() ? FirebaseGetResult::FromError(call.errorMessage()) + : FirebaseGetResult::FromResponse(call.rawResponse()); } -FirebaseCall Firebase::push(const String& path, const String& value) { - return FirebaseCall(host_, auth_, "POST", path, value, &http_); +FirebasePushResult Firebase::push(const String& path, const String& value) { + FirebaseCall call(host_, auth_, "POST", path, value, &http_); + return call.isError() ? FirebasePushResult::FromError(call.errorMessage()) + : FirebasePushResult::FromResponse(call.rawResponse()); } -FirebaseCall Firebase::remove(const String& path) { - return FirebaseCall(host_, auth_, "DELETE", path, &http_); +FirebaseRemoveResult Firebase::remove(const String& path) { + FirebaseCall call(host_, auth_, "DELETE", path, &http_); + if (call.isError()) { + return FirebaseRemoveResult::FromError(call.errorMessage()); + } + // Remove is only complete if returned status is OK(200). + if (!call.isOk()) { + return FirebaseRemoveResult::FromError( + "Remove " + path + " returned with status " + call.httpStatus()); + } + return FirebaseRemoveResult::Ok(); } FirebaseEventStream Firebase::stream(const String& path) { @@ -59,7 +106,6 @@ FirebaseEventStream Firebase::stream(const String& path) { } /* FirebaseCall */ - FirebaseCall::FirebaseCall(const String& host, const String& auth, const char* method, const String& path, const String& value, HTTPClient* http) : http_(http) { @@ -154,4 +200,56 @@ String FirebaseEventStream::errorMessage() const { return error_message_; } +FirebaseEventStream::operator bool() { + return !isError() && connected(); +} + +/* FirebaseResult */ + +FirebaseResult::FirebaseResult(const String& error_message) + : is_error_(true), error_message_(error_message) {} + +FirebaseResult::FirebaseResult() {} + +FirebaseResult::operator bool() const { + return !isError(); +} + +bool FirebaseResult::isError() const { + return is_error_; +} + +const String& FirebaseResult::errorMessage() const { + return error_message_; +} + +/* FirebaseRemoveResult */ + +FirebaseRemoveResult::FirebaseRemoveResult(const String& error_message) + : FirebaseResult(error_message) {} + +FirebaseRemoveResult::FirebaseRemoveResult() {} + + +/* FirebasePushResult */ + +FirebasePushResult::FirebasePushResult(const String& error_message) + : FirebaseResult(error_message) {} + +FirebasePushResult::FirebasePushResult() {} + +const String& FirebasePushResult::name() const { + return name_; +} + +/* FirebaseGetResult */ + +FirebaseGetResult::FirebaseGetResult(const String& error_message) + : FirebaseResult(error_message) {} + +FirebaseGetResult::FirebaseGetResult() {} + +const String& FirebaseGetResult::rawResponse() { + return response_; +} diff --git a/Firebase.h b/Firebase.h index 958a21e8..d19be6b9 100644 --- a/Firebase.h +++ b/Firebase.h @@ -25,9 +25,9 @@ #include #include -//TODO(edcoyne) split these into multiple files. - -class FirebaseCall; +class FirebaseGetResult; +class FirebasePushResult; +class FirebaseRemoveResult; class FirebaseEventStream; // Primary client to the Firebase backend. @@ -38,55 +38,103 @@ class Firebase { // Fetch result at "path" to a local variable. If the value is too large you will exceed // local memory. - FirebaseCall get(const String& path); + FirebaseGetResult get(const String& path); // Add new value to list at "path", will return child name of new item. - FirebaseCall push(const String& path, const String& value); + FirebasePushResult push(const String& path, const String& value); // Deletes value at "path" from server. - FirebaseCall remove(const String& path); + FirebaseRemoveResult remove(const String& path); // Starts a stream of events that effect object at "path". FirebaseEventStream stream(const String& path); private: + + int sendRequest(const char* method, const String& path, const String& value); + HTTPClient http_; String host_; String auth_; }; -class FirebaseCall { + +// Result from a Firebase call. +class FirebaseResult { public: - FirebaseCall(const String& host, const String& auth, - const char* method, const String& path, const String& value, - HTTPClient* http); - FirebaseCall(const String& host, const String& auth, - const char* method, const String& path, - HTTPClient* http); + // Constructor for error result. + FirebaseResult(const String& error_message); + // Constructor if no error. + FirebaseResult(); + // True if no error. + operator bool() const; - // True if there was an error completing call. bool isError() const; - String errorMessage() const; - // True if http status code is 200(OK). - bool isOk() const; + const String& errorMessage() const; + + private: + bool is_error_ = false; + String error_message_; +}; - // Message sent back from Firebase backend. This pulls value to local memory, - // be careful if value can be large. - String rawResponse(); +class FirebaseRemoveResult : public FirebaseResult { + public: + static FirebaseRemoveResult FromError(const String& error_message) { + return FirebaseRemoveResult(error_message); + } - int httpStatus() const { - return status_; + static FirebaseRemoveResult Ok() { + return FirebaseRemoveResult(); } private: - FirebaseCall(HTTPClient* http); + FirebaseRemoveResult(const String& error_message); + FirebaseRemoveResult(); +}; - HTTPClient* http_; +class FirebasePushResult : public FirebaseResult { + public: + static FirebasePushResult FromError(const String& error_message) { + return FirebasePushResult(error_message); + } - int status_; - String error_message_; + static FirebasePushResult FromResponse(const String& response) { + FirebasePushResult result; + // TODO(edcoyne): add json parsing to get name object. + result.name_ = response; + return result; + } + + const String& name() const; + + private: + FirebasePushResult(const String& error_message); + FirebasePushResult(); + + String name_; +}; + +class FirebaseGetResult : public FirebaseResult { + public: + static FirebaseGetResult FromError(const String& error_message) { + return FirebaseGetResult(error_message); + } + + static FirebaseGetResult FromResponse(const String& response) { + FirebaseGetResult result; + result.response_ = response; + return result; + } + + const String& rawResponse(); + + private: + FirebaseGetResult(const String& error_message); + FirebaseGetResult(); + + String response_; }; class FirebaseEventStream { @@ -108,6 +156,9 @@ class FirebaseEventStream { // True if there is an event available. bool available(); + // True if no error and stream is connected. + operator bool(); + // True if there was an error. bool isError() const; String errorMessage() const; diff --git a/examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino b/examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino index 87e35a40..a9cdab8c 100644 --- a/examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino +++ b/examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino @@ -38,20 +38,20 @@ void setup() { Serial.println(WiFi.localIP()); // add a new entry. - FirebaseCall push = fbase.push("/logs", "{\".sv\": \"timestamp\"}"); - if (push.isError()) { + FirebasePushResult push = fbase.push("/logs", "{\".sv\": \"timestamp\"}"); + if (!push) { Serial.println("Firebase request failed"); Serial.println(push.errorMessage()); return; } // print response. - Serial.println(push.rawResponse()); + Serial.println(push.name()); // print all entries. - FirebaseCall fetch = fbase.get("/logs"); - if (!fetch.isError()) { - Serial.println(fetch.rawResponse()); + FirebaseGetResult get = fbase.get("/logs"); + if (get) { + Serial.println(get.rawResponse()); } } diff --git a/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino b/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino index 919902e9..6bcac3dc 100644 --- a/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino +++ b/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino @@ -47,7 +47,7 @@ void setup() { void loop() { static FirebaseEventStream stream = fbase.stream("/bitcoin"); - if (stream.isError()) { + if (!stream) { Serial.println("streaming error"); Serial.println(stream.errorMessage()); } From 2ac7ab345183d8604ad9f78f598a0e1eb663a09c Mon Sep 17 00:00:00 2001 From: Ed Coyne Date: Wed, 27 Jan 2016 20:07:54 -0800 Subject: [PATCH 12/15] Remove unused method declaration. --- Firebase.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Firebase.h b/Firebase.h index d19be6b9..c7eb28be 100644 --- a/Firebase.h +++ b/Firebase.h @@ -36,8 +36,7 @@ class Firebase { Firebase(const String& host); Firebase& auth(const String& auth); - // Fetch result at "path" to a local variable. If the value is too large you will exceed - // local memory. + // Fetch result at "path". FirebaseGetResult get(const String& path); // Add new value to list at "path", will return child name of new item. @@ -58,8 +57,7 @@ class Firebase { String auth_; }; - -// Result from a Firebase call. +// Base class for Results from a Firebase call. class FirebaseResult { public: // Constructor for error result. @@ -145,8 +143,6 @@ class FirebaseEventStream { PATCH }; - FirebaseEventStream(const String& host, const String& auth, const String& path); - // Read next event in stream. Event read(String& event); From 357a1d0736b595bfa786002cabd9489f343b199c Mon Sep 17 00:00:00 2001 From: Johan Euphrosine Date: Thu, 28 Jan 2016 15:06:10 -0800 Subject: [PATCH 13/15] firebase: refactor the refactoring --- Firebase.cpp | 254 ++++++------------ Firebase.h | 154 +++++------ .../FirebasePush_ESP8266.ino | 24 +- .../FirebaseStream_ESP8266.ino | 8 +- 4 files changed, 166 insertions(+), 274 deletions(-) diff --git a/Firebase.cpp b/Firebase.cpp index 9d86a712..69cd0150 100644 --- a/Firebase.cpp +++ b/Firebase.cpp @@ -19,7 +19,7 @@ namespace { const char* kFirebaseFingerprint = "7A 54 06 9B DC 7A 25 B3 86 8D 66 53 48 2C 0B 96 42 C7 B3 0A"; const uint16_t kFirebasePort = 443; -String makeUrl(const String& path, const String& auth) { +String makeFirebaseURL(const String& path, const String& auth) { String url; if (path[0] != '/') { url = "/"; @@ -31,40 +31,6 @@ String makeUrl(const String& path, const String& auth) { return url; } -class FirebaseCall { - public: - FirebaseCall(const String& host, const String& auth, - const char* method, const String& path, const String& value, - HTTPClient* http); - FirebaseCall(const String& host, const String& auth, - const char* method, const String& path, - HTTPClient* http); - - - // True if there was an error completing call. - bool isError() const; - String errorMessage() const; - - // True if http status code is 200(OK). - bool isOk() const; - - // Message sent back from Firebase backend. This pulls value to local memory, - // be careful if value can be large. - String rawResponse(); - - int httpStatus() const { - return status_; - } - - private: - FirebaseCall(HTTPClient* http); - - HTTPClient* http_; - - int status_; - String error_message_; -}; - } // namespace Firebase::Firebase(const String& host) : host_(host) { @@ -76,180 +42,122 @@ Firebase& Firebase::auth(const String& auth) { return *this; } -FirebaseGetResult Firebase::get(const String& path) { - FirebaseCall call(host_, auth_, "GET", path, &http_); - return call.isError() ? FirebaseGetResult::FromError(call.errorMessage()) - : FirebaseGetResult::FromResponse(call.rawResponse()); +FirebaseGet Firebase::get(const String& path) { + return FirebaseGet(host_, auth_, path, &http_); } -FirebasePushResult Firebase::push(const String& path, const String& value) { - FirebaseCall call(host_, auth_, "POST", path, value, &http_); - return call.isError() ? FirebasePushResult::FromError(call.errorMessage()) - : FirebasePushResult::FromResponse(call.rawResponse()); +FirebasePush Firebase::push(const String& path, const String& value) { + return FirebasePush(host_, auth_, path, value, &http_); } -FirebaseRemoveResult Firebase::remove(const String& path) { - FirebaseCall call(host_, auth_, "DELETE", path, &http_); - if (call.isError()) { - return FirebaseRemoveResult::FromError(call.errorMessage()); - } - // Remove is only complete if returned status is OK(200). - if (!call.isOk()) { - return FirebaseRemoveResult::FromError( - "Remove " + path + " returned with status " + call.httpStatus()); - } - return FirebaseRemoveResult::Ok(); +FirebaseRemove Firebase::remove(const String& path) { + return FirebaseRemove(host_, auth_, path, &http_); } -FirebaseEventStream Firebase::stream(const String& path) { - return FirebaseEventStream(host_, auth_, path); +FirebaseStream Firebase::stream(const String& path) { + return FirebaseStream(host_, auth_, path); // stream doesn't reuse http client. } -/* FirebaseCall */ +// FirebaseCall FirebaseCall::FirebaseCall(const String& host, const String& auth, - const char* method, const String& path, const String& value, - HTTPClient* http) : http_(http) { - const String url = makeUrl(path, auth); - http_->begin(host.c_str(), kFirebasePort, url.c_str(), true, kFirebaseFingerprint); - status_ = http_->sendRequest(method, (uint8_t*)value.c_str(), value.length()); - if (isError()) { - error_message_ = String(method) + " " + url + ": " + HTTPClient::errorToString(status_); + const char* method, const String& path, + const String& data, HTTPClient* http) { + if (!http) { + http = &http_; } -} -FirebaseCall::FirebaseCall(const String& host, const String& auth, - const char* method, const String& path, - HTTPClient* http) : FirebaseCall(host, auth, method, path, "", http) { -} + String url = makeFirebaseURL(path, auth); + http->setReuse(true); + http->begin(host, kFirebasePort, url, true, kFirebaseFingerprint); -bool FirebaseCall::isOk() const { - return status_ == HTTP_CODE_OK; -} + bool followRedirect = false; + if (method == "STREAM") { + method = "GET"; + http->addHeader("Accept", "text/event-stream"); + followRedirect = true; + } -bool FirebaseCall::isError() const { - return status_ < 0; -} + if (followRedirect) { + const char* headers[] = {"Location"}; + http->collectHeaders(headers, 1); + } + + int status = http->sendRequest(method, (uint8_t*)data.c_str(), data.length()); + + // TODO: Add a max redirect check + if (followRedirect) { + while (status == HTTP_CODE_TEMPORARY_REDIRECT) { + String location = http->header("Location"); + http->setReuse(false); + http->end(); + http->setReuse(true); + http->begin(location, kFirebaseFingerprint); + status = http->sendRequest(method, (uint8_t*)NULL, 0); + } + } -String FirebaseCall::errorMessage() const { - return error_message_; -} + if (status != 200) { + error_ = FirebaseError(status, String(method) + " " + url + ": " + HTTPClient::errorToString(status)); + } -String FirebaseCall::rawResponse() { - return http_->getString(); + // if not streaming. + if (!followRedirect) { + response_ = http->getString(); + } } -/* FirebaseEventStream */ +// FirebaseGet +FirebaseGet::FirebaseGet(const String& host, const String& auth, + const String& path, + HTTPClient* http) + : FirebaseCall(host, auth, "GET", path, "", http) { -FirebaseEventStream::FirebaseEventStream(const String& host, const String& auth, - const String& path) { - const String url = makeUrl(path, auth); - http_.setReuse(true); - http_.begin(host.c_str(), kFirebasePort, url.c_str(), true, - kFirebaseFingerprint); - const char* headers[] = {"Location"}; - http_.collectHeaders(headers, 1); - http_.addHeader("Accept", "text/event-stream"); - status_ = http_.sendRequest("GET", (uint8_t*)NULL, 0); - - String location; - // TODO(proppy): Add a max redirect check - while (status_ == HTTP_CODE_TEMPORARY_REDIRECT) { - location = http_.header("Location"); - http_.setReuse(false); - http_.end(); - http_.setReuse(true); - http_.begin(location, kFirebaseFingerprint); - status_ = http_.sendRequest("GET", (uint8_t*)NULL, 0); + if (!error()) { + // TODO: parse json + json_ = response(); } +} - if (status_ != 200) { - error_message_ = "stream " + location + ": " - + HTTPClient::errorToString(status_); +// FirebasePush +FirebasePush::FirebasePush(const String& host, const String& auth, + const String& path, const String& value, + HTTPClient* http) + : FirebaseCall(host, auth, "POST", path, value, http) { + if (!error()) { + // TODO: parse name + name_ = response(); } } -bool FirebaseEventStream::connected() { - return http_.connected(); +// FirebasePush +FirebaseRemove::FirebaseRemove(const String& host, const String& auth, + const String& path, + HTTPClient* http) + : FirebaseCall(host, auth, "DELETE", path, "", http) { } -bool FirebaseEventStream::available() { +// FirebaseStream +FirebaseStream::FirebaseStream(const String& host, const String& auth, + const String& path) + : FirebaseCall(host, auth, "STREAM", path) { +} + +bool FirebaseStream::available() { return http_.getStreamPtr()->available(); } -FirebaseEventStream::Event FirebaseEventStream::read(String& event) { +FirebaseStream::Event FirebaseStream::read(String& event) { auto client = http_.getStreamPtr(); Event type; String typeStr = client->readStringUntil('\n').substring(7); if (typeStr == "put") { - type = FirebaseEventStream::Event::PUT; + type = Event::PUT; } else if (typeStr == "patch") { - type = FirebaseEventStream::Event::PATCH; + type = Event::PATCH; } else { - type = FirebaseEventStream::Event::UNKNOWN; + type = Event::UNKNOWN; } event = client->readStringUntil('\n').substring(6); client->readStringUntil('\n'); // consume separator return type; } - -bool FirebaseEventStream::isError() const { - return status_ < 0; -} - -String FirebaseEventStream::errorMessage() const { - return error_message_; -} - -FirebaseEventStream::operator bool() { - return !isError() && connected(); -} - -/* FirebaseResult */ - -FirebaseResult::FirebaseResult(const String& error_message) - : is_error_(true), error_message_(error_message) {} - -FirebaseResult::FirebaseResult() {} - -FirebaseResult::operator bool() const { - return !isError(); -} - -bool FirebaseResult::isError() const { - return is_error_; -} - -const String& FirebaseResult::errorMessage() const { - return error_message_; -} - -/* FirebaseRemoveResult */ - -FirebaseRemoveResult::FirebaseRemoveResult(const String& error_message) - : FirebaseResult(error_message) {} - -FirebaseRemoveResult::FirebaseRemoveResult() {} - - -/* FirebasePushResult */ - -FirebasePushResult::FirebasePushResult(const String& error_message) - : FirebaseResult(error_message) {} - -FirebasePushResult::FirebasePushResult() {} - -const String& FirebasePushResult::name() const { - return name_; -} - -/* FirebaseGetResult */ - -FirebaseGetResult::FirebaseGetResult(const String& error_message) - : FirebaseResult(error_message) {} - -FirebaseGetResult::FirebaseGetResult() {} - -const String& FirebaseGetResult::rawResponse() { - return response_; -} - diff --git a/Firebase.h b/Firebase.h index c7eb28be..99927568 100644 --- a/Firebase.h +++ b/Firebase.h @@ -25,10 +25,10 @@ #include #include -class FirebaseGetResult; -class FirebasePushResult; -class FirebaseRemoveResult; -class FirebaseEventStream; +class FirebaseGet; +class FirebasePush; +class FirebaseRemove; +class FirebaseStream; // Primary client to the Firebase backend. class Firebase { @@ -37,106 +37,96 @@ class Firebase { Firebase& auth(const String& auth); // Fetch result at "path". - FirebaseGetResult get(const String& path); + FirebaseGet get(const String& path); - // Add new value to list at "path", will return child name of new item. - FirebasePushResult push(const String& path, const String& value); + // Add new value to list at "path", will return key for the new item. + FirebasePush push(const String& path, const String& value); - // Deletes value at "path" from server. - FirebaseRemoveResult remove(const String& path); + // Deletes value at "path" from firebase. + FirebaseRemove remove(const String& path); - // Starts a stream of events that effect object at "path". - FirebaseEventStream stream(const String& path); + // Starts a stream of events that affect object at "path". + FirebaseStream stream(const String& path); private: - - int sendRequest(const char* method, const String& path, const String& value); - HTTPClient http_; String host_; String auth_; }; -// Base class for Results from a Firebase call. -class FirebaseResult { +class FirebaseError { public: - // Constructor for error result. - FirebaseResult(const String& error_message); - // Constructor if no error. - FirebaseResult(); - - // True if no error. - operator bool() const; - - bool isError() const; - - const String& errorMessage() const; - - private: - bool is_error_ = false; - String error_message_; + FirebaseError() {} + FirebaseError(int code, const String& message) : code_(code), message_(message) { + } + operator bool() const { return code_ != 0; } + int code() const { return code_; } + const String& message() const { return message_; } + private: + int code_ = 0; + String message_ = ""; }; -class FirebaseRemoveResult : public FirebaseResult { +class FirebaseCall { public: - static FirebaseRemoveResult FromError(const String& error_message) { - return FirebaseRemoveResult(error_message); + FirebaseCall(const String& host, const String& auth, + const char* method, const String& path, + const String& data = "", + HTTPClient* http = NULL); + const FirebaseError& error() const { + return error_; } - - static FirebaseRemoveResult Ok() { - return FirebaseRemoveResult(); + const String& response() { + return response_; } - - private: - FirebaseRemoveResult(const String& error_message); - FirebaseRemoveResult(); + protected: + HTTPClient http_; + FirebaseError error_; + String response_; }; -class FirebasePushResult : public FirebaseResult { +class FirebaseGet : public FirebaseCall { public: - static FirebasePushResult FromError(const String& error_message) { - return FirebasePushResult(error_message); - } - - static FirebasePushResult FromResponse(const String& response) { - FirebasePushResult result; - // TODO(edcoyne): add json parsing to get name object. - result.name_ = response; - return result; + FirebaseGet(const String& host, const String& auth, + const String& path, HTTPClient* http = NULL); + + const String& json() const { + return json_; } - const String& name() const; - private: - FirebasePushResult(const String& error_message); - FirebasePushResult(); - - String name_; + String json_; }; -class FirebaseGetResult : public FirebaseResult { +class FirebasePush : public FirebaseCall { public: - static FirebaseGetResult FromError(const String& error_message) { - return FirebaseGetResult(error_message); - } + FirebasePush(const String& host, const String& auth, + const String& path, const String& value, HTTPClient* http = NULL); - static FirebaseGetResult FromResponse(const String& response) { - FirebaseGetResult result; - result.response_ = response; - return result; + const String& name() const { + return name_; } - const String& rawResponse(); - private: - FirebaseGetResult(const String& error_message); - FirebaseGetResult(); + String name_; +}; - String response_; +class FirebaseRemove : public FirebaseCall { + public: + FirebaseRemove(const String& host, const String& auth, + const String& path, HTTPClient* http = NULL); }; -class FirebaseEventStream { + +class FirebaseStream : public FirebaseCall { public: + FirebaseStream(const String& host, const String& auth, + const String &path); + + // True if there is an event available. + bool available(); + + // event type. enum Event { UNKNOWN, PUT, @@ -144,25 +134,15 @@ class FirebaseEventStream { }; // Read next event in stream. - Event read(String& event); - - // True if connected to backend. - bool connected(); - - // True if there is an event available. - bool available(); - - // True if no error and stream is connected. - operator bool(); - - // True if there was an error. - bool isError() const; - String errorMessage() const; + Event read(String& event); + const FirebaseError& error() const { + return _error; + } + private: HTTPClient http_; - int status_; - String error_message_; + FirebaseError _error; }; #endif // firebase_h diff --git a/examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino b/examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino index a9cdab8c..1f30d68c 100644 --- a/examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino +++ b/examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino @@ -38,21 +38,25 @@ void setup() { Serial.println(WiFi.localIP()); // add a new entry. - FirebasePushResult push = fbase.push("/logs", "{\".sv\": \"timestamp\"}"); - if (!push) { - Serial.println("Firebase request failed"); - Serial.println(push.errorMessage()); + FirebasePush push = fbase.push("/logs", "{\".sv\": \"timestamp\"}"); + if (push.error()) { + Serial.println("Firebase push failed"); + Serial.println(push.error().message()); return; } - // print response. + // print key. Serial.println(push.name()); - - // print all entries. - FirebaseGetResult get = fbase.get("/logs"); - if (get) { - Serial.println(get.rawResponse()); + + // get all entries. + FirebaseGet get = fbase.get("/logs"); + if (get.error()) { + Serial.println("Firebase get failed"); + Serial.println(push.error().message()); + return; } + // print json. + Serial.println(get.json()); } void loop() { diff --git a/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino b/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino index 6bcac3dc..92743215 100644 --- a/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino +++ b/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino @@ -46,10 +46,10 @@ void setup() { void loop() { - static FirebaseEventStream stream = fbase.stream("/bitcoin"); - if (!stream) { + static FirebaseStream stream = fbase.stream("/bitcoin"); + if (!stream.error()) { Serial.println("streaming error"); - Serial.println(stream.errorMessage()); + Serial.println(stream.error().message()); } if (stream.available()) { @@ -57,7 +57,7 @@ void loop() { auto type = stream.read(event); Serial.print("event: "); Serial.println(type); - if (type != FirebaseEventStream::Event::UNKNOWN) { + if (type != FirebaseEvent::Event::UNKNOWN) { Serial.print("data: "); Serial.println(event); From b286b19b10c034435d01884006e57f0fd60a2e38 Mon Sep 17 00:00:00 2001 From: Johan Euphrosine Date: Fri, 29 Jan 2016 10:58:43 -0800 Subject: [PATCH 14/15] firebase: fix stream example --- Firebase.cpp | 42 +++++++++---------- Firebase.h | 12 ++++-- .../FirebasePush_ESP8266.ino | 4 +- .../FirebaseStream_ESP8266.ino | 7 ++-- 4 files changed, 35 insertions(+), 30 deletions(-) diff --git a/Firebase.cpp b/Firebase.cpp index 69cd0150..6752811a 100644 --- a/Firebase.cpp +++ b/Firebase.cpp @@ -55,44 +55,41 @@ FirebaseRemove Firebase::remove(const String& path) { } FirebaseStream Firebase::stream(const String& path) { - return FirebaseStream(host_, auth_, path); // stream doesn't reuse http client. + // TODO: create new client dedicated to stream. + return FirebaseStream(host_, auth_, path, &http_); } // FirebaseCall FirebaseCall::FirebaseCall(const String& host, const String& auth, const char* method, const String& path, - const String& data, HTTPClient* http) { - if (!http) { - http = &http_; - } - + const String& data, HTTPClient* http) : http_(http) { String url = makeFirebaseURL(path, auth); - http->setReuse(true); - http->begin(host, kFirebasePort, url, true, kFirebaseFingerprint); + http_->setReuse(true); + http_->begin(host, kFirebasePort, url, true, kFirebaseFingerprint); bool followRedirect = false; if (method == "STREAM") { method = "GET"; - http->addHeader("Accept", "text/event-stream"); + http_->addHeader("Accept", "text/event-stream"); followRedirect = true; } if (followRedirect) { const char* headers[] = {"Location"}; - http->collectHeaders(headers, 1); + http_->collectHeaders(headers, 1); } - int status = http->sendRequest(method, (uint8_t*)data.c_str(), data.length()); + int status = http_->sendRequest(method, (uint8_t*)data.c_str(), data.length()); // TODO: Add a max redirect check if (followRedirect) { while (status == HTTP_CODE_TEMPORARY_REDIRECT) { - String location = http->header("Location"); - http->setReuse(false); - http->end(); - http->setReuse(true); - http->begin(location, kFirebaseFingerprint); - status = http->sendRequest(method, (uint8_t*)NULL, 0); + String location = http_->header("Location"); + http_->setReuse(false); + http_->end(); + http_->setReuse(true); + http_->begin(location, kFirebaseFingerprint); + status = http_->sendRequest("GET", (uint8_t*)NULL, 0); } } @@ -102,7 +99,7 @@ FirebaseCall::FirebaseCall(const String& host, const String& auth, // if not streaming. if (!followRedirect) { - response_ = http->getString(); + response_ = http_->getString(); } } @@ -138,16 +135,17 @@ FirebaseRemove::FirebaseRemove(const String& host, const String& auth, // FirebaseStream FirebaseStream::FirebaseStream(const String& host, const String& auth, - const String& path) - : FirebaseCall(host, auth, "STREAM", path) { + const String& path, + HTTPClient* http) + : FirebaseCall(host, auth, "STREAM", path, "", http) { } bool FirebaseStream::available() { - return http_.getStreamPtr()->available(); + return http_->getStreamPtr()->available(); } FirebaseStream::Event FirebaseStream::read(String& event) { - auto client = http_.getStreamPtr(); + auto client = http_->getStreamPtr(); Event type; String typeStr = client->readStringUntil('\n').substring(7); if (typeStr == "put") { diff --git a/Firebase.h b/Firebase.h index 99927568..aac4e86c 100644 --- a/Firebase.h +++ b/Firebase.h @@ -46,6 +46,8 @@ class Firebase { FirebaseRemove remove(const String& path); // Starts a stream of events that affect object at "path". + // TODO: fix FirebaseStream lifecycle + // https://github.com/esp8266/Arduino/issues/500 FirebaseStream stream(const String& path); private: @@ -69,6 +71,7 @@ class FirebaseError { class FirebaseCall { public: + FirebaseCall() {} FirebaseCall(const String& host, const String& auth, const char* method, const String& path, const String& data = "", @@ -80,13 +83,14 @@ class FirebaseCall { return response_; } protected: - HTTPClient http_; + HTTPClient* http_; FirebaseError error_; String response_; }; class FirebaseGet : public FirebaseCall { public: + FirebaseGet() {} FirebaseGet(const String& host, const String& auth, const String& path, HTTPClient* http = NULL); @@ -100,6 +104,7 @@ class FirebaseGet : public FirebaseCall { class FirebasePush : public FirebaseCall { public: + FirebasePush() {} FirebasePush(const String& host, const String& auth, const String& path, const String& value, HTTPClient* http = NULL); @@ -113,6 +118,7 @@ class FirebasePush : public FirebaseCall { class FirebaseRemove : public FirebaseCall { public: + FirebaseRemove() {} FirebaseRemove(const String& host, const String& auth, const String& path, HTTPClient* http = NULL); }; @@ -120,8 +126,9 @@ class FirebaseRemove : public FirebaseCall { class FirebaseStream : public FirebaseCall { public: + FirebaseStream() {} FirebaseStream(const String& host, const String& auth, - const String &path); + const String& path, HTTPClient* http = NULL); // True if there is an event available. bool available(); @@ -141,7 +148,6 @@ class FirebaseStream : public FirebaseCall { } private: - HTTPClient http_; FirebaseError _error; }; diff --git a/examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino b/examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino index 1f30d68c..c1bd368e 100644 --- a/examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino +++ b/examples/FirebasePush_ESP8266/FirebasePush_ESP8266.ino @@ -20,8 +20,8 @@ #include // create firebase client. -Firebase fbase = Firebase("example.firebaseio.com") - .auth("secret_or_token"); +Firebase fbase("example.firebaseio.com") + .auth("secret_or_token"); void setup() { Serial.begin(9600); diff --git a/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino b/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino index 92743215..3f24bfec 100644 --- a/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino +++ b/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino @@ -24,7 +24,8 @@ #define OLED_RESET 10 Adafruit_SSD1306 display(OLED_RESET); -Firebase fbase = Firebase("publicdata-cryptocurrency.firebaseio.com"); +Firebase fbase("publicdata-cryptocurrency.firebaseio.com"); +FirebaseStream stream; void setup() { Serial.begin(9600); @@ -42,11 +43,11 @@ void setup() { Serial.println(); Serial.print("connected: "); Serial.println(WiFi.localIP()); + stream = fbase.stream("/bitcoin"); } void loop() { - static FirebaseStream stream = fbase.stream("/bitcoin"); if (!stream.error()) { Serial.println("streaming error"); Serial.println(stream.error().message()); @@ -57,7 +58,7 @@ void loop() { auto type = stream.read(event); Serial.print("event: "); Serial.println(type); - if (type != FirebaseEvent::Event::UNKNOWN) { + if (type != FirebaseStream::Event::UNKNOWN) { Serial.print("data: "); Serial.println(event); From e8ac1e9ceef623e62371b2fafd4e1879478a832a Mon Sep 17 00:00:00 2001 From: Johan Euphrosine Date: Fri, 29 Jan 2016 11:37:09 -0800 Subject: [PATCH 15/15] examples/stream: fix error handling --- examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino b/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino index 3f24bfec..73b17149 100644 --- a/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino +++ b/examples/FirebaseStream_ESP8266/FirebaseStream_ESP8266.ino @@ -48,7 +48,7 @@ void setup() { void loop() { - if (!stream.error()) { + if (stream.error()) { Serial.println("streaming error"); Serial.println(stream.error().message()); }