Skip to content

Commit dac9d4c

Browse files
committed
FirebaseArduino: switch to ArduinoHttpClient
This allow the library to be portable across arduino core implementing the Client Interface. Fixes FirebaseExtended#344
1 parent de3874e commit dac9d4c

6 files changed

+155
-55
lines changed

README.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ The Arduino library is [under heavy development](https://github.com/googlesample
2020
- [FirebaseArduino API Reference](http://firebase-arduino.readthedocs.io/)
2121

2222
## Dependencies
23-
- 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.
23+
24+
FirebaseArduino depends on the following libraries:
25+
- [ArduinoJson library](https://github.com/bblanchon/ArduinoJson)
26+
- [ArduinoHttpClient library](https://github.com/arduino-libraries/ArduinoHttpClient/)
27+
28+
Please either use Library Manager or download specific version of the library from github.
2429

2530
## Disclaimer
2631

library.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ sentence=Library for communicating with Firebase.
66
paragraph=This library simplifies the process of communicating with Firebase. It hides the complexity of authentication and json parsing.
77
category=Communication
88
url=https://github.com/googlesamples/firebase-arduino
9-
architectures=esp8266
9+
architectures=esp8266,esp32

src/FirebaseArduino.cpp

+120-37
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,26 @@
1616

1717
#include "FirebaseArduino.h"
1818

19-
// This is needed to compile std::string on esp8266.
20-
template class std::basic_string<char>;
19+
const char* kApplicationType = "application/json";
20+
const uint16_t kFirebasePort = 443;
21+
const int kStatusOK = 200;
22+
const int kStatusTemporaryRedirect = 307;
23+
24+
String makeFirebaseURI(const String& path, const String& auth) {
25+
String uri;
26+
if (path[0] != '/') {
27+
uri = "/";
28+
}
29+
uri += path;
30+
uri += ".json";
31+
if (auth.length() > 0) {
32+
uri += "?auth=";
33+
uri += auth;
34+
}
35+
return uri;
36+
}
37+
38+
FirebaseArduino::FirebaseArduino(Client& client) : client_(client) {}
2139

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

2745
void FirebaseArduino::initStream() {
2846
if (stream_http_.get() == nullptr) {
29-
stream_http_.reset(FirebaseHttpClient::create());
30-
stream_http_->setReuseConnection(true);
31-
stream_.reset(new FirebaseStream(stream_http_));
47+
stream_http_.reset(new HttpClient(client_, host_, kFirebasePort));
48+
stream_http_->connectionKeepAlive();
3249
}
3350
}
3451

3552
void FirebaseArduino::initRequest() {
3653
if (req_http_.get() == nullptr) {
37-
req_http_.reset(FirebaseHttpClient::create());
38-
req_http_->setReuseConnection(true);
39-
req_.reset(new FirebaseRequest(req_http_));
54+
req_http_.reset(new HttpClient(client_, host_, kFirebasePort));
55+
req_http_->connectionKeepAlive();
4056
}
4157
}
4258

@@ -62,11 +78,20 @@ String FirebaseArduino::push(const String& path, const JsonVariant& value) {
6278
char * buf = new char[size];
6379
value.printTo(buf, size);
6480
initRequest();
65-
int status = req_.get()->sendRequest(host_, auth_, "POST", path.c_str(), buf);
66-
error_ = req_.get()->error();
67-
const char* name = req_.get()->json()["name"].as<const char*>();
81+
String uri = makeFirebaseURI(path, auth_);
82+
int err = req_http_->post(uri.c_str(), kApplicationType, buf);
83+
if (err != 0) {
84+
error_ = FirebaseError(err, "HTTP request failed");
85+
return "";
86+
}
87+
int statusCode = req_http_->responseStatusCode();
88+
if (statusCode != kStatusOK) {
89+
error_ = FirebaseError(statusCode, "PUT request failed");
90+
return "";
91+
}
6892
delete buf;
69-
return name;
93+
StaticJsonBuffer<FIREBASE_JSONBUFFER_SIZE> jsonBuffer;
94+
return jsonBuffer.parseObject(req_http_->responseBody())["name"];
7095
}
7196

7297
void FirebaseArduino::setInt(const String& path, int value) {
@@ -91,31 +116,50 @@ void FirebaseArduino::set(const String& path, const JsonVariant& value) {
91116
char* buf= new char[size];
92117
value.printTo(buf, size);
93118
initRequest();
94-
req_.get()->sendRequest(host_, auth_, "PUT", path.c_str(), buf);
95-
error_ = req_.get()->error();
119+
String uri = makeFirebaseURI(path, auth_);
120+
int err = req_http_->put(uri.c_str(), kApplicationType, buf);
121+
if (err != 0) {
122+
error_ = FirebaseError(err, "HTTP request failed");
123+
return;
124+
}
125+
int statusCode = req_http_->responseStatusCode();
126+
if (statusCode != kStatusOK) {
127+
error_ = FirebaseError(statusCode, "POST request failed");
128+
return;
129+
}
130+
req_http_->responseBody(); // consume body;
96131
delete buf;
97132
}
98133

99134
void FirebaseArduino::getRequest(const String& path) {
100135
initRequest();
101-
req_.get()->sendRequest(host_, auth_, "GET", path.c_str());
102-
error_ = req_.get()->error();
136+
String uri = makeFirebaseURI(path, auth_);
137+
int err = req_http_->get(uri.c_str());
138+
if (err != 0) {
139+
error_ = FirebaseError(err, "HTTP request failed");
140+
return;
141+
}
142+
int statusCode = req_http_->responseStatusCode();
143+
if (statusCode != kStatusOK) {
144+
error_ = FirebaseError(err, "GET request failed");
145+
return;
146+
}
103147
}
104148

105149
FirebaseObject FirebaseArduino::get(const String& path) {
106150
getRequest(path);
107151
if (failed()) {
108152
return FirebaseObject{""};
109153
}
110-
return FirebaseObject(req_.get()->response().c_str());
154+
return FirebaseObject(req_http_->responseBody());
111155
}
112156

113157
int FirebaseArduino::getInt(const String& path) {
114158
getRequest(path);
115159
if (failed()) {
116160
return 0;
117161
}
118-
return FirebaseObject(req_.get()->response().c_str()).getInt();
162+
return FirebaseObject(req_http_->responseBody()).getInt();
119163
}
120164

121165

@@ -124,56 +168,88 @@ float FirebaseArduino::getFloat(const String& path) {
124168
if (failed()) {
125169
return 0.0f;
126170
}
127-
return FirebaseObject(req_.get()->response().c_str()).getFloat();
171+
return FirebaseObject(req_http_->responseBody()).getFloat();
128172
}
129173

130174
String FirebaseArduino::getString(const String& path) {
131175
getRequest(path);
132176
if (failed()) {
133177
return "";
134178
}
135-
return FirebaseObject(req_.get()->response().c_str()).getString();
179+
return FirebaseObject(req_http_->responseBody()).getString();
136180
}
137181

138182
bool FirebaseArduino::getBool(const String& path) {
139183
getRequest(path);
140184
if (failed()) {
141185
return "";
142186
}
143-
return FirebaseObject(req_.get()->response().c_str()).getBool();
187+
return FirebaseObject(req_http_->responseBody()).getBool();
144188
}
189+
145190
void FirebaseArduino::remove(const String& path) {
146191
initRequest();
147-
req_.get()->sendRequest(host_, auth_, "DELETE", path.c_str());
148-
error_ = req_.get()->error();
192+
String uri = makeFirebaseURI(path, auth_);
193+
int err = req_http_->del(uri.c_str());
194+
if (err != 0) {
195+
error_ = FirebaseError(err, "HTTP request failed");
196+
return;
197+
}
198+
int statusCode = req_http_->responseStatusCode();
199+
if (statusCode != kStatusOK) {
200+
error_ = FirebaseError(statusCode, "PUT request failed");
201+
return;
202+
}
203+
req_http_->responseBody(); // consume body;
149204
}
150205

151206
void FirebaseArduino::stream(const String& path) {
152207
initStream();
153-
stream_.get()->startStreaming(host_, auth_, path.c_str());
154-
error_ = stream_.get()->error();
208+
String uri = makeFirebaseURI(path, auth_);
209+
stream_http_->beginRequest();
210+
stream_http_->get(uri.c_str());
211+
stream_http_->sendHeader("Accept", "text/event-stream");
212+
stream_http_->endRequest();
213+
214+
int statusCode = stream_http_->responseStatusCode();
215+
if (statusCode != kStatusOK) {
216+
error_ = FirebaseError(statusCode, "STREAM request failed");
217+
return;
218+
}
219+
220+
if (statusCode == kStatusTemporaryRedirect) {
221+
while(stream_http_->headerAvailable()) {
222+
if (stream_http_->readHeaderName() == "Location") {
223+
String location = stream_http_->readHeaderValue();
224+
int hostnameStart = location.indexOf(':')+2;
225+
int hostnameEnd = location.indexOf('/', hostnameStart);
226+
String hostname = location.substring(hostnameStart, hostnameEnd);
227+
String path = location.substring(hostnameEnd);
228+
client_.stop();
229+
stream_http_.reset(new HttpClient(client_, hostname, kFirebasePort));
230+
stream_http_->connectionKeepAlive();
231+
stream(path);
232+
return;
233+
}
234+
}
235+
}
155236
}
156237

157238
bool FirebaseArduino::available() {
158239
if (stream_http_.get() == nullptr) {
159-
return 0;
240+
return false;
160241
}
161-
auto client = stream_http_.get()->getStreamPtr();
162-
return (client == nullptr) ? false : client->available();
242+
return stream_http_->available();
163243
}
164244

165245
FirebaseObject FirebaseArduino::readEvent() {
166246
if (stream_http_.get() == nullptr) {
167247
return FirebaseObject("");
168248
}
169-
auto client = stream_http_.get()->getStreamPtr();
170-
if (client == nullptr) {
171-
return FirebaseObject("");
172-
}
173-
String type = client->readStringUntil('\n').substring(7);;
174-
String event = client->readStringUntil('\n').substring(6);
175-
client->readStringUntil('\n'); // consume separator
176-
FirebaseObject obj = FirebaseObject(event.c_str());
249+
String type = stream_http_->readStringUntil('\n').substring(7);;
250+
String event = stream_http_->readStringUntil('\n').substring(6);
251+
stream_http_->readStringUntil('\n'); // consume separator
252+
FirebaseObject obj = FirebaseObject(event);
177253
obj.getJsonVariant().asObject()["type"] = type.c_str();
178254
return obj;
179255
}
@@ -190,4 +266,11 @@ const String& FirebaseArduino::error() {
190266
return error_.message().c_str();
191267
}
192268

193-
FirebaseArduino Firebase;
269+
270+
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
271+
272+
#include <WiFiClientSecure.h>
273+
WiFiClientSecure client;
274+
FirebaseArduino Firebase(client);
275+
276+
#endif

src/FirebaseArduino.h

+20-16
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919

2020
#include <string>
2121

22-
#include "Firebase.h"
22+
#include <ArduinoHttpClient.h>
23+
24+
#include "FirebaseError.h"
2325
#include "FirebaseObject.h"
2426

2527
/**
@@ -31,13 +33,15 @@
3133
*/
3234
class FirebaseArduino {
3335
public:
36+
FirebaseArduino(Client& client);
37+
3438
/**
3539
* Must be called first. This initialize the client with the given
3640
* firebase host and credentials.
3741
* \param host Your firebase db host, usually X.firebaseio.com.
3842
* \param auth Optional credentials for the db, a secret or token.
3943
*/
40-
virtual void begin(const String& host, const String& auth = "");
44+
void begin(const String& host, const String& auth = "");
4145

4246
/**
4347
* Appends the integer value to the node at path.
@@ -77,7 +81,7 @@ class FirebaseArduino {
7781
* \param value String value that you wish to append to the node.
7882
* \return The unique key of the new child node.
7983
*/
80-
virtual String pushString(const String& path, const String& value);
84+
String pushString(const String& path, const String& value);
8185

8286
/**
8387
* Appends the JSON data to the node at path.
@@ -123,7 +127,7 @@ class FirebaseArduino {
123127
* \param path The path inside of your db to the node you wish to update.
124128
* \param value String value that you wish to write.
125129
*/
126-
virtual void setString(const String& path, const String& value);
130+
void setString(const String& path, const String& value);
127131

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

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

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

196200
/**
197201
* Checks if there are new events available. This is only meaningful once
198202
* stream() has been called.
199203
* \return If a new event is ready.
200204
*/
201-
virtual bool available();
205+
bool available();
202206

203207
/**
204208
* Reads the next event in a stream. This is only meaningful once stream() has
205209
* been called.
206210
* \return FirebaseObject will have ["type"] that describes the event type, ["path"]
207211
* that describes the effected path and ["data"] that was updated.
208212
*/
209-
virtual FirebaseObject readEvent();
213+
FirebaseObject readEvent();
210214

211215
/**
212216
* \return Whether the last command was successful.
@@ -221,15 +225,15 @@ class FirebaseArduino {
221225
/**
222226
* \return Error message from last command if failed() is true.
223227
*/
224-
virtual const String& error();
228+
const String& error();
225229
private:
226-
std::string host_;
227-
std::string auth_;
230+
Client& client_;
231+
232+
String host_;
233+
String auth_;
228234
FirebaseError error_;
229-
std::shared_ptr<FirebaseHttpClient> req_http_;
230-
std::shared_ptr<FirebaseRequest> req_;
231-
std::shared_ptr<FirebaseHttpClient> stream_http_;
232-
std::shared_ptr<FirebaseStream> stream_;
235+
std::shared_ptr<HttpClient> req_http_;
236+
std::shared_ptr<HttpClient> stream_http_;
233237

234238
void initStream();
235239
void initRequest();

src/FirebaseObject.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ FirebaseObject::FirebaseObject(const char* data) : data_{data} {
2626
// See: https://github.com/bblanchon/ArduinoJson/issues/279
2727
}
2828

29+
FirebaseObject::FirebaseObject(const String& data) : FirebaseObject(data.c_str()) {}
30+
2931
bool FirebaseObject::getBool(const String& path) const {
3032
JsonVariant variant = getJsonVariant(path);
3133
if (!variant.is<bool>()) {

0 commit comments

Comments
 (0)