Skip to content
This repository was archived by the owner on Mar 17, 2025. It is now read-only.

FirebaseArduino: switch to ArduinoHttpClient #353

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ The Arduino library is [under heavy development](https://github.com/googlesample
- [FirebaseArduino API Reference](http://firebase-arduino.readthedocs.io/)

## Dependencies
- FirebaseArduino now depends on [ArduinoJson library](https://github.com/bblanchon/ArduinoJson) instead of containing it's own version of it. Please either use Library Manager or download specific version of the library from github.

FirebaseArduino depends on the following libraries:
- [ArduinoJson library](https://github.com/bblanchon/ArduinoJson)
- [ArduinoHttpClient library](https://github.com/arduino-libraries/ArduinoHttpClient/)

Please either use Library Manager or download specific version of the library from github.

## Disclaimer

Expand Down
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ sentence=Library for communicating with Firebase.
paragraph=This library simplifies the process of communicating with Firebase. It hides the complexity of authentication and json parsing.
category=Communication
url=https://github.com/googlesamples/firebase-arduino
architectures=esp8266
architectures=esp8266,esp32
157 changes: 120 additions & 37 deletions src/FirebaseArduino.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,26 @@

#include "FirebaseArduino.h"

// This is needed to compile std::string on esp8266.
template class std::basic_string<char>;
const char* kApplicationType = "application/json";
const uint16_t kFirebasePort = 443;
const int kStatusOK = 200;
const int kStatusTemporaryRedirect = 307;

String makeFirebaseURI(const String& path, const String& auth) {
String uri;
if (path[0] != '/') {
uri = "/";
}
uri += path;
uri += ".json";
if (auth.length() > 0) {
uri += "?auth=";
uri += auth;
}
return uri;
}

FirebaseArduino::FirebaseArduino(Client& client) : client_(client) {}

void FirebaseArduino::begin(const String& host, const String& auth) {
host_ = host.c_str();
Expand All @@ -26,17 +44,15 @@ void FirebaseArduino::begin(const String& host, const String& auth) {

void FirebaseArduino::initStream() {
if (stream_http_.get() == nullptr) {
stream_http_.reset(FirebaseHttpClient::create());
stream_http_->setReuseConnection(true);
stream_.reset(new FirebaseStream(stream_http_));
stream_http_.reset(new HttpClient(client_, host_, kFirebasePort));
stream_http_->connectionKeepAlive();
}
}

void FirebaseArduino::initRequest() {
if (req_http_.get() == nullptr) {
req_http_.reset(FirebaseHttpClient::create());
req_http_->setReuseConnection(true);
req_.reset(new FirebaseRequest(req_http_));
req_http_.reset(new HttpClient(client_, host_, kFirebasePort));
req_http_->connectionKeepAlive();
}
}

Expand All @@ -62,11 +78,20 @@ String FirebaseArduino::push(const String& path, const JsonVariant& value) {
char * buf = new char[size];
value.printTo(buf, size);
initRequest();
int status = req_.get()->sendRequest(host_, auth_, "POST", path.c_str(), buf);
error_ = req_.get()->error();
const char* name = req_.get()->json()["name"].as<const char*>();
String uri = makeFirebaseURI(path, auth_);
int err = req_http_->post(uri.c_str(), kApplicationType, buf);
if (err != 0) {
error_ = FirebaseError(err, "HTTP request failed");
return "";
}
int statusCode = req_http_->responseStatusCode();
if (statusCode != kStatusOK) {
error_ = FirebaseError(statusCode, "PUT request failed");
return "";
}
delete buf;
return name;
StaticJsonBuffer<FIREBASE_JSONBUFFER_SIZE> jsonBuffer;
return jsonBuffer.parseObject(req_http_->responseBody())["name"];
}

void FirebaseArduino::setInt(const String& path, int value) {
Expand All @@ -91,31 +116,50 @@ void FirebaseArduino::set(const String& path, const JsonVariant& value) {
char* buf= new char[size];
value.printTo(buf, size);
initRequest();
req_.get()->sendRequest(host_, auth_, "PUT", path.c_str(), buf);
error_ = req_.get()->error();
String uri = makeFirebaseURI(path, auth_);
int err = req_http_->put(uri.c_str(), kApplicationType, buf);
if (err != 0) {
error_ = FirebaseError(err, "HTTP request failed");
return;
}
int statusCode = req_http_->responseStatusCode();
if (statusCode != kStatusOK) {
error_ = FirebaseError(statusCode, "POST request failed");
return;
}
req_http_->responseBody(); // consume body;
delete buf;
}

void FirebaseArduino::getRequest(const String& path) {
initRequest();
req_.get()->sendRequest(host_, auth_, "GET", path.c_str());
error_ = req_.get()->error();
String uri = makeFirebaseURI(path, auth_);
int err = req_http_->get(uri.c_str());
if (err != 0) {
error_ = FirebaseError(err, "HTTP request failed");
return;
}
int statusCode = req_http_->responseStatusCode();
if (statusCode != kStatusOK) {
error_ = FirebaseError(err, "GET request failed");
return;
}
}

FirebaseObject FirebaseArduino::get(const String& path) {
getRequest(path);
if (failed()) {
return FirebaseObject{""};
}
return FirebaseObject(req_.get()->response().c_str());
return FirebaseObject(req_http_->responseBody());
}

int FirebaseArduino::getInt(const String& path) {
getRequest(path);
if (failed()) {
return 0;
}
return FirebaseObject(req_.get()->response().c_str()).getInt();
return FirebaseObject(req_http_->responseBody()).getInt();
}


Expand All @@ -124,56 +168,88 @@ float FirebaseArduino::getFloat(const String& path) {
if (failed()) {
return 0.0f;
}
return FirebaseObject(req_.get()->response().c_str()).getFloat();
return FirebaseObject(req_http_->responseBody()).getFloat();
}

String FirebaseArduino::getString(const String& path) {
getRequest(path);
if (failed()) {
return "";
}
return FirebaseObject(req_.get()->response().c_str()).getString();
return FirebaseObject(req_http_->responseBody()).getString();
}

bool FirebaseArduino::getBool(const String& path) {
getRequest(path);
if (failed()) {
return "";
}
return FirebaseObject(req_.get()->response().c_str()).getBool();
return FirebaseObject(req_http_->responseBody()).getBool();
}

void FirebaseArduino::remove(const String& path) {
initRequest();
req_.get()->sendRequest(host_, auth_, "DELETE", path.c_str());
error_ = req_.get()->error();
String uri = makeFirebaseURI(path, auth_);
int err = req_http_->del(uri.c_str());
if (err != 0) {
error_ = FirebaseError(err, "HTTP request failed");
return;
}
int statusCode = req_http_->responseStatusCode();
if (statusCode != kStatusOK) {
error_ = FirebaseError(statusCode, "PUT request failed");
return;
}
req_http_->responseBody(); // consume body;
}

void FirebaseArduino::stream(const String& path) {
initStream();
stream_.get()->startStreaming(host_, auth_, path.c_str());
error_ = stream_.get()->error();
String uri = makeFirebaseURI(path, auth_);
stream_http_->beginRequest();
stream_http_->get(uri.c_str());
stream_http_->sendHeader("Accept", "text/event-stream");
stream_http_->endRequest();

int statusCode = stream_http_->responseStatusCode();
if (statusCode != kStatusOK) {
error_ = FirebaseError(statusCode, "STREAM request failed");
return;
}

if (statusCode == kStatusTemporaryRedirect) {
while(stream_http_->headerAvailable()) {
if (stream_http_->readHeaderName() == "Location") {
String location = stream_http_->readHeaderValue();
int hostnameStart = location.indexOf(':')+2;
int hostnameEnd = location.indexOf('/', hostnameStart);
String hostname = location.substring(hostnameStart, hostnameEnd);
String path = location.substring(hostnameEnd);
client_.stop();
stream_http_.reset(new HttpClient(client_, hostname, kFirebasePort));
stream_http_->connectionKeepAlive();
stream(path);
return;
}
}
}
}

bool FirebaseArduino::available() {
if (stream_http_.get() == nullptr) {
return 0;
return false;
}
auto client = stream_http_.get()->getStreamPtr();
return (client == nullptr) ? false : client->available();
return stream_http_->available();
}

FirebaseObject FirebaseArduino::readEvent() {
if (stream_http_.get() == nullptr) {
return FirebaseObject("");
}
auto client = stream_http_.get()->getStreamPtr();
if (client == nullptr) {
return FirebaseObject("");
}
String type = client->readStringUntil('\n').substring(7);;
String event = client->readStringUntil('\n').substring(6);
client->readStringUntil('\n'); // consume separator
FirebaseObject obj = FirebaseObject(event.c_str());
String type = stream_http_->readStringUntil('\n').substring(7);;
String event = stream_http_->readStringUntil('\n').substring(6);
stream_http_->readStringUntil('\n'); // consume separator
FirebaseObject obj = FirebaseObject(event);
obj.getJsonVariant().asObject()["type"] = type.c_str();
return obj;
}
Expand All @@ -190,4 +266,11 @@ const String& FirebaseArduino::error() {
return error_.message().c_str();
}

FirebaseArduino Firebase;

#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)

#include <WiFiClientSecure.h>
WiFiClientSecure client;
FirebaseArduino Firebase(client);

#endif
36 changes: 20 additions & 16 deletions src/FirebaseArduino.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@

#include <string>

#include "Firebase.h"
#include <ArduinoHttpClient.h>

#include "FirebaseError.h"
#include "FirebaseObject.h"

/**
Expand All @@ -31,13 +33,15 @@
*/
class FirebaseArduino {
public:
FirebaseArduino(Client& client);

/**
* Must be called first. This initialize the client with the given
* firebase host and credentials.
* \param host Your firebase db host, usually X.firebaseio.com.
* \param auth Optional credentials for the db, a secret or token.
*/
virtual void begin(const String& host, const String& auth = "");
void begin(const String& host, const String& auth = "");

/**
* Appends the integer value to the node at path.
Expand Down Expand Up @@ -77,7 +81,7 @@ class FirebaseArduino {
* \param value String value that you wish to append to the node.
* \return The unique key of the new child node.
*/
virtual String pushString(const String& path, const String& value);
String pushString(const String& path, const String& value);

/**
* Appends the JSON data to the node at path.
Expand Down Expand Up @@ -123,7 +127,7 @@ class FirebaseArduino {
* \param path The path inside of your db to the node you wish to update.
* \param value String value that you wish to write.
*/
virtual void setString(const String& path, const String& value);
void setString(const String& path, const String& value);

/**
* Writes the JSON data to the node located at path.
Expand Down Expand Up @@ -157,7 +161,7 @@ class FirebaseArduino {
* \param path The path to the node you wish to retrieve.
* \return The string value located at that path. Will only be populated if success() is true.
*/
virtual String getString(const String& path);
String getString(const String& path);

/**
* Gets the boolean value located at path.
Expand All @@ -181,7 +185,7 @@ class FirebaseArduino {
* \param path The path to the node you wish to remove,
* including all of its children.
*/
virtual void remove(const String& path);
void remove(const String& path);

/**
* Starts streaming any changes made to the node located at path, including
Expand All @@ -191,22 +195,22 @@ class FirebaseArduino {
* monitoring available() and calling readEvent() to get new events.
* \param path The path inside of your db to the node you wish to monitor.
*/
virtual void stream(const String& path);
void stream(const String& path);

/**
* Checks if there are new events available. This is only meaningful once
* stream() has been called.
* \return If a new event is ready.
*/
virtual bool available();
bool available();

/**
* Reads the next event in a stream. This is only meaningful once stream() has
* been called.
* \return FirebaseObject will have ["type"] that describes the event type, ["path"]
* that describes the effected path and ["data"] that was updated.
*/
virtual FirebaseObject readEvent();
FirebaseObject readEvent();

/**
* \return Whether the last command was successful.
Expand All @@ -221,15 +225,15 @@ class FirebaseArduino {
/**
* \return Error message from last command if failed() is true.
*/
virtual const String& error();
const String& error();
private:
std::string host_;
std::string auth_;
Client& client_;

String host_;
String auth_;
FirebaseError error_;
std::shared_ptr<FirebaseHttpClient> req_http_;
std::shared_ptr<FirebaseRequest> req_;
std::shared_ptr<FirebaseHttpClient> stream_http_;
std::shared_ptr<FirebaseStream> stream_;
std::shared_ptr<HttpClient> req_http_;
std::shared_ptr<HttpClient> stream_http_;

void initStream();
void initRequest();
Expand Down
2 changes: 2 additions & 0 deletions src/FirebaseObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ FirebaseObject::FirebaseObject(const char* data) : data_{data} {
// See: https://github.com/bblanchon/ArduinoJson/issues/279
}

FirebaseObject::FirebaseObject(const String& data) : FirebaseObject(data.c_str()) {}

bool FirebaseObject::getBool(const String& path) const {
JsonVariant variant = getJsonVariant(path);
if (!variant.is<bool>()) {
Expand Down
Loading