Skip to content

Commit d91e5ec

Browse files
author
Vladimir Veljkovic
committed
Publish no longer returns NULL on HTTP status code error.
Instead, one can use the new API `get_last_http_status_code_class()` to check if there was a "non-success" HTTP status code returned from Pubnub network. This helps in diagnosing the reason for the failure, as user can read the HTTP response body (which contains the description, like "Quota exceeded").
1 parent 4ac1db9 commit d91e5ec

File tree

4 files changed

+91
-48
lines changed

4 files changed

+91
-48
lines changed

PubNubDefs.h

Lines changed: 66 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ inline int strncasecmp(const char *s1, const char *s2, size_t n) {
6262
* The user application sees only the JSON body, not the timetoken.
6363
* As soon as the body ends, PubSubclient reads the rest of HTTP reply
6464
* itself and disconnects. The stored timetoken is used in the next call
65-
* to the PubSub::subscribe method.
65+
* to the PubNub::subscribe() method.
6666
*/
6767
class PubSubClient : public PubNub_BASE_CLIENT {
6868
public:
@@ -174,13 +174,39 @@ class PubNub {
174174
* @return boolean whether begin() was successful.
175175
*/
176176
bool begin(const char *publish_key, const char *subscribe_key, const char *origin = "pubsub.pubnub.com") {
177-
d_publish_key = publish_key;
178-
d_subscribe_key = subscribe_key;
179-
d_origin = origin;
180-
d_uuid = 0;
181-
d_auth = 0;
177+
d_publish_key = publish_key;
178+
d_subscribe_key = subscribe_key;
179+
d_origin = origin;
180+
d_uuid = 0;
181+
d_auth = 0;
182+
d_last_http_status_code_class = http_scc_unknown;
182183
}
183184

185+
/**
186+
* HTTP status code class. It is defined by the first digit of the
187+
* HTTP status code.
188+
*
189+
* @see RFC 7231 Section 6
190+
*/
191+
enum http_status_code_class {
192+
/** This is not defined in the RFC, we use it to indicate
193+
"none/unknown" */
194+
http_scc_unknown = 0,
195+
/** The request was received, continuing process */
196+
http_scc_informational = 1,
197+
/** The request was successfully received, understood, and
198+
accepted */
199+
http_scc_success = 2,
200+
/** Further action needs to be taken in order to complete the
201+
request */
202+
http_scc_redirection = 3,
203+
/** The request contains bad syntax or cannot be fulfilled */
204+
http_scc_client_error = 4,
205+
/** The server failed to fulfill an apparently valid
206+
request */
207+
http_scc_server_error = 5
208+
};
209+
184210
/**
185211
* Set the UUID identification of PubNub client. This is useful
186212
* e.g. for presence identification.
@@ -204,19 +230,25 @@ class PubNub {
204230
}
205231

206232
/**
207-
* Publish
208-
*
209-
* Send a message (assumed to be well-formed JSON) to a given channel.
233+
* Publish/Send a message (assumed to be well-formed JSON) to a
234+
* given channel.
210235
*
211236
* Note that the reply can be obtained using code like:
212237
213-
client = publish("demo", "\"lala\"");
214-
if (!client) return; // error
238+
client = PubNub.publish("demo", "\"lala\"");
239+
if (!client) {
240+
Serial.println("Failed to publish, got no response from PubNub");
241+
return;
242+
}
243+
if (PubNub.get_last_http_status_code_class() != PubNub::http_scc_success) {
244+
Serial.print("Got HTTP status code error from PubNub, class: ");
245+
Serial.print((int)PubNub.get_last_http_status_code_class(), DEC);
246+
}
215247
while (client->connected()) {
216248
// More sophisticated code will want to add timeout handling here
217249
while (client->connected() && !client->available()) ; // wait
218250
char c = client->read();
219-
Serial.print(c);
251+
Serial.print(c);
220252
}
221253
client->stop();
222254
@@ -235,12 +267,10 @@ class PubNub {
235267
inline PubNub_BASE_CLIENT *publish(const char *channel, const char *message, int timeout = 30);
236268

237269
/**
238-
* Subscribe
239-
*
240-
* Listen for a message on a given channel. The function will block
241-
* and return when a message arrives. Typically, you will run this
242-
* function from loop() function to keep listening for messages
243-
* indefinitely.
270+
* Subscribe/Listen for a message on a given channel. The function
271+
* will block and return when a message arrives. Typically, you
272+
* will run this function from loop() function to keep listening
273+
* for messages indefinitely.
244274
*
245275
* As a reply, you will get a JSON array with messages, e.g.:
246276
* ["msg1",{msg2:"x"}]
@@ -265,6 +295,14 @@ class PubNub {
265295
* @return string Stream-ish object with reply message or 0 on error. */
266296
inline PubNub_BASE_CLIENT *history(const char *channel, int limit = 10, int timeout = 310);
267297

298+
/** Returns the HTTP status code class of the last PubNub
299+
transaction. If the transaction failed without getting a
300+
(HTTP) response, it will be "unknown".
301+
*/
302+
inline http_status_code_class get_last_http_status_code_class() const {
303+
return d_last_http_status_code_class;
304+
}
305+
268306
private:
269307
enum PubNub_BH {
270308
PubNub_BH_OK,
@@ -279,6 +317,9 @@ class PubNub {
279317
const char *d_origin;
280318
const char *d_uuid;
281319
const char *d_auth;
320+
321+
/// The HTTP status code class of the last PubNub transaction
322+
http_status_code_class d_last_http_status_code_class;
282323

283324
PubNub_BASE_CLIENT publish_client, history_client;
284325
PubSubClient subscribe_client;
@@ -450,6 +491,7 @@ inline PubNub_BASE_CLIENT *PubNub::publish(const char *channel, const char *mess
450491
return 0;
451492
}
452493

494+
d_last_http_status_code_class = http_scc_unknown;
453495
client.flush();
454496
client.print("GET /publish/");
455497
client.print(d_publish_key);
@@ -491,16 +533,12 @@ inline PubNub_BASE_CLIENT *PubNub::publish(const char *channel, const char *mess
491533
enum PubNub::PubNub_BH ret = this->_request_bh(client, t_start, timeout, have_param ? '&' : '?');
492534
switch (ret) {
493535
case PubNub_BH_OK:
494-
/* Success and reached body, return handle to the client
495-
* for further perusal. */
496536
return &client;
497537
case PubNub_BH_ERROR:
498-
/* Failure. */
499538
client.stop();
500539
while (client.connected()) ;
501540
return 0;
502541
case PubNub_BH_TIMEOUT:
503-
/* Time out. Try again. */
504542
client.stop();
505543
while (client.connected()) ;
506544
goto retry;
@@ -524,6 +562,7 @@ inline PubSubClient *PubNub::subscribe(const char *channel, int timeout)
524562
return 0;
525563
}
526564

565+
d_last_http_status_code_class = http_scc_unknown;
527566
client.flush();
528567
client.print("GET /subscribe/");
529568
client.print(d_subscribe_key);
@@ -549,10 +588,8 @@ inline PubSubClient *PubNub::subscribe(const char *channel, int timeout)
549588
/* Success and reached body. We need to eat '[' first,
550589
* as our API contract is to return only the "message body"
551590
* part of reply from subscribe. */
552-
if (!client.wait_for_data()
553-
|| client.read() != '[') {
554-
/* Something unexpected. */
555-
DBGprintln("Unexpected body in subscribe");
591+
if (!client.wait_for_data() || client.read() != '[') {
592+
DBGprintln("Unexpected body in subscribe response");
556593
client.stop();
557594
while (client.connected()) ;
558595
return 0;
@@ -565,13 +602,11 @@ inline PubSubClient *PubNub::subscribe(const char *channel, int timeout)
565602
return &client;
566603

567604
case PubNub_BH_ERROR:
568-
/* Failure. */
569605
client.stop();
570606
while (client.connected()) ;
571607
return 0;
572608

573609
case PubNub_BH_TIMEOUT:
574-
/* Time out. Try again. */
575610
client.stop();
576611
while (client.connected()) ;
577612
goto retry;
@@ -592,6 +627,7 @@ inline PubNub_BASE_CLIENT *PubNub::history(const char *channel, int limit, int t
592627
return 0;
593628
}
594629

630+
d_last_http_status_code_class = http_scc_unknown;
595631
client.flush();
596632
client.print("GET /history/");
597633
client.print(d_subscribe_key);
@@ -603,16 +639,12 @@ inline PubNub_BASE_CLIENT *PubNub::history(const char *channel, int limit, int t
603639
enum PubNub::PubNub_BH ret = this->_request_bh(client, t_start, timeout, '?');
604640
switch (ret) {
605641
case PubNub_BH_OK:
606-
/* Success and reached body, return handle to the client
607-
* for further perusal. */
608642
return &client;
609643
case PubNub_BH_ERROR:
610-
/* Failure. */
611644
client.stop();
612645
while (client.connected()) ;
613646
return 0;
614647
case PubNub_BH_TIMEOUT:
615-
/* Time out. Try again. */
616648
client.stop();
617649
while (client.connected()) ;
618650
goto retry;
@@ -653,15 +685,8 @@ inline enum PubNub::PubNub_BH PubNub::_request_bh(PubNub_BASE_CLIENT &client, un
653685
/* Now, first digit of HTTP code. */
654686
WAIT();
655687
char c = client.read();
656-
if (c != '2') {
657-
/* HTTP code that is NOT 2xx means trouble.
658-
* kthxbai */
659-
DBGprint("Wrong HTTP status first digit ASCII code: ");
660-
DBGprint((int) c, DEC);
661-
DBGprintln(" in bottom half");
662-
return PubNub_BH_ERROR;
663-
}
664-
688+
d_last_http_status_code_class = static_cast<http_status_code_class>(c - '0');
689+
665690
/* Now, we enter in a state machine that shall guide us through
666691
* the remaining headers to the beginning of the body. */
667692
enum {

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,23 @@ The origin parameter is optional, defaulting to "pubsub.pubnub.com".
7070

7171
Send a message (assumed to be well-formed JSON) to a given channel.
7272

73-
Returns NULL in case of error, instead a pointer to an instance of
73+
Returns `NULL` in case of error, instead a pointer to an instance of
7474
`Pubnub_BASE_CLIENT` class that you can use to read the reply to the
7575
publish command. If you don't care about it, call ``client->stop()``
7676
right away. The default `Pubnub_BASE_CLIENT` is `EthernetClient`,
7777
and the second most used one is `WiFiClient`, but, _any_ compatible
7878
client class will do.
7979

80+
Since v2.1.0, if Pubnub responds with a HTTP status code indicating a
81+
failure, this will not return `NULL`. Of course, `NULL` will still be
82+
returned for other errors, like, DNS failure, network failure, etc.
83+
If you care, you should check the HTTP status code class, like:
84+
85+
if (PubNub.get_last_http_status_code_class() != PubNub::http_scc_success) {
86+
Serial.print("Got HTTP status code error from PubNub, class: ");
87+
Serial.print((int)PubNub.get_last_http_status_code_class(), DEC);
88+
}
89+
8090
The timeout parameter is optional, with a sensible default. See also a
8191
note about timeouts below.
8292

examples/PubNubWifi101/PubNubWifi101.ino

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@
1818
#include <PubNub.h>
1919

2020
static char ssid[] = "your-wifi-network"; // your network SSID (name)
21-
static char pass[] = "password"; // your network password
21+
static char pass[] = "your-wifi-password"; // your network password
2222
int status = WL_IDLE_STATUS; // the Wifi radio's status
2323

24-
const static char pubkey[] = "your-pub-key";
25-
const static char subkey[] = "your-sub-key";
26-
const static char channel[] = "hello_world";
24+
const static char pubkey[] = "demo"; // your publish key
25+
const static char subkey[] = "demo"; // your subscribe key
26+
const static char channel[] = "hello_world"; // channel to use
2727

2828

2929
void setup() {
@@ -53,7 +53,7 @@ void loop() {
5353
/* Publish */
5454

5555
WiFiClient *client;
56-
56+
5757
char msg[] = "\"Yo!\"";
5858

5959
client = PubNub.publish(channel, msg);
@@ -63,7 +63,15 @@ void loop() {
6363
delay(1000);
6464
return;
6565
}
66+
if (PubNub.get_last_http_status_code_class() != PubNub::http_scc_success) {
67+
Serial.print("Got HTTP status code error from PubNub, class: ");
68+
Serial.print(PubNub.get_last_http_status_code_class(), DEC);
69+
}
70+
while (client->available()) {
71+
Serial.write(client->read());
72+
}
6673
client->stop();
74+
Serial.println("---");
6775
}
6876

6977

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=Pubnub
2-
version=2.0.3
2+
version=2.1.0
33
author=Vladimir Veljkovic <[email protected]>
44
maintainer=Vladimir Veljkovic <[email protected]>
55
sentence=Pubnub SDK for Arduino.

0 commit comments

Comments
 (0)