Build Realtime Apps With The Real-Time Network - the mbed PubNub API+SDK

PubNub

The PubNub library enables your mbed board to communicate with the world via the PubNub cloud messaging system.

The library provides a PubNub class that is tied to a particular set of keys and offers methods that correspond to the appropriate API methods - publish, subscribe, history, etc. The JSON encoded messages are passed as raw strings to conserve memory, but at your option, you can use e.g. picojson library to deal with JSON. The API is synchronous - use multiple rtos threads to talk to PubNub on the background.

Getting Started

Can't wait to try it out? Connect your mbed application board and proceed to the demo project:

Import programPubNubDemo

Reference demo of the PubNub library for the mbed application board - control your board over the internet!

Library Usage

Import library

Public Member Functions

PubNub (const char *publish_key, const char *subscribe_key, const char *origin="http://pubsub.pubnub.com")
Init a Pubnub Client context.
PubNubRes publish (const char *channel, const char *message, char **reply=NULL)
Publish API call.
PubNubRes subscribe (const char *channel, char **reply)
Subscribe API call.
PubNubRes history (const char *channel, char **reply, int *replysize, int limit=10)
History API call.
PubNubRes time (char *ts)
Time API call.
Committer:
pasky
Date:
Sun Mar 02 01:32:54 2014 +0000
Revision:
0:9858347c382d
Child:
5:1c98f24712e1
Initial version: Basic working API + short docs with examples. Please refer to PubNubDemo program for a reference usage code.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
pasky 0:9858347c382d 1 #include <cstring>
pasky 0:9858347c382d 2
pasky 0:9858347c382d 3 #include "mbed.h"
pasky 0:9858347c382d 4 #include "EthernetInterface.h"
pasky 0:9858347c382d 5
pasky 0:9858347c382d 6 #include "PubNub.h"
pasky 0:9858347c382d 7
pasky 0:9858347c382d 8
pasky 0:9858347c382d 9 #define DBG if (0) // change to if (1) to enable debug prints on USB serial
pasky 0:9858347c382d 10
pasky 0:9858347c382d 11
pasky 0:9858347c382d 12 /* We roll our own HTTP communication stack as donatien's HTTPClient has
pasky 0:9858347c382d 13 * some hard limits on URL length and this will allow a more frugal RAM
pasky 0:9858347c382d 14 * usage for us. */
pasky 0:9858347c382d 15
pasky 0:9858347c382d 16 /* Also, we pass messages as serialized JSON strings instead of parsed
pasky 0:9858347c382d 17 * structures. The available JSON parsers (picojson in particular) are
pasky 0:9858347c382d 18 * heavy on many tiny memory allocations, which can get very troublesome
pasky 0:9858347c382d 19 * with mbed's malloc() - we get very high memory fragmentation. In many
pasky 0:9858347c382d 20 * cases, working with raw JSON is just fine, and in our demo example,
pasky 0:9858347c382d 21 * we still show picojson usage for end-user parsing of complex incoming
pasky 0:9858347c382d 22 * messages. */
pasky 0:9858347c382d 23
pasky 0:9858347c382d 24
pasky 0:9858347c382d 25 /** Custom no-frills HTTP stream. */
pasky 0:9858347c382d 26
pasky 0:9858347c382d 27 class PubNubHTTP: public TCPSocketConnection {
pasky 0:9858347c382d 28 public:
pasky 0:9858347c382d 29 /* Connect http socket to origin. */
pasky 0:9858347c382d 30 PubNubRes http_connect(const char *hostname);
pasky 0:9858347c382d 31
pasky 0:9858347c382d 32 /* These methods are used to send the request type and URL. */
pasky 0:9858347c382d 33 /* Send a given NUL-terminated string over the http socket. */
pasky 0:9858347c382d 34 void sendstr(const char *string);
pasky 0:9858347c382d 35 /* Send a given NUL-terminated string over the http socket, URL encoded. */
pasky 0:9858347c382d 36 void sendstr_urlenc(const char *string);
pasky 0:9858347c382d 37
pasky 0:9858347c382d 38 /* A common HTTP request "bottom half" - send the rest of headers
pasky 0:9858347c382d 39 * and wait for reply + receive. */
pasky 0:9858347c382d 40 PubNubRes http_request_bh(const char *host, char **reply, int *replylen);
pasky 0:9858347c382d 41
pasky 0:9858347c382d 42 protected:
pasky 0:9858347c382d 43 /* These methods represent sub-stages of http_request_bh().
pasky 0:9858347c382d 44 * They share the state of m_replybuf and m_replylen. */
pasky 0:9858347c382d 45 PubNubRes http_send_headers(const char *host);
pasky 0:9858347c382d 46 PubNubRes http_recv_headers();
pasky 0:9858347c382d 47 PubNubRes http_recv_content();
pasky 0:9858347c382d 48
pasky 0:9858347c382d 49 char *m_replybuf;
pasky 0:9858347c382d 50 int m_replylen;
pasky 0:9858347c382d 51 int m_content_length;
pasky 0:9858347c382d 52 };
pasky 0:9858347c382d 53
pasky 0:9858347c382d 54 PubNubRes
pasky 0:9858347c382d 55 PubNubHTTP::http_connect(const char *hostname)
pasky 0:9858347c382d 56 {
pasky 0:9858347c382d 57 int ret = connect(hostname, 80);
pasky 0:9858347c382d 58 if (ret < 0) {
pasky 0:9858347c382d 59 close();
pasky 0:9858347c382d 60 return PNR_IO_ERROR;
pasky 0:9858347c382d 61 }
pasky 0:9858347c382d 62 return PNR_OK;
pasky 0:9858347c382d 63 }
pasky 0:9858347c382d 64
pasky 0:9858347c382d 65 void
pasky 0:9858347c382d 66 PubNubHTTP::sendstr(const char *s)
pasky 0:9858347c382d 67 {
pasky 0:9858347c382d 68 send_all((char *) s, strlen(s));
pasky 0:9858347c382d 69 }
pasky 0:9858347c382d 70
pasky 0:9858347c382d 71 void
pasky 0:9858347c382d 72 PubNubHTTP::sendstr_urlenc(const char *s)
pasky 0:9858347c382d 73 {
pasky 0:9858347c382d 74 while (s[0]) {
pasky 0:9858347c382d 75 /* RFC 3986 Unreserved characters plus few
pasky 0:9858347c382d 76 * safe reserved ones. */
pasky 0:9858347c382d 77 size_t okspan = strspn(s, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~" ",=:;@[]");
pasky 0:9858347c382d 78 if (okspan > 0) {
pasky 0:9858347c382d 79 send_all((char *) s, okspan);
pasky 0:9858347c382d 80 s += okspan;
pasky 0:9858347c382d 81 }
pasky 0:9858347c382d 82 if (s[0]) {
pasky 0:9858347c382d 83 /* %-encode a non-ok character. */
pasky 0:9858347c382d 84 char enc[3] = {'%'};
pasky 0:9858347c382d 85 enc[1] = "0123456789ABCDEF"[s[0] / 16];
pasky 0:9858347c382d 86 enc[2] = "0123456789ABCDEF"[s[0] % 16];
pasky 0:9858347c382d 87 send_all((char *) enc, 3);
pasky 0:9858347c382d 88 s++;
pasky 0:9858347c382d 89 }
pasky 0:9858347c382d 90 }
pasky 0:9858347c382d 91 }
pasky 0:9858347c382d 92
pasky 0:9858347c382d 93 PubNubRes
pasky 0:9858347c382d 94 PubNubHTTP::http_request_bh(const char *host, char **reply, int *replylen)
pasky 0:9858347c382d 95 {
pasky 0:9858347c382d 96 m_replybuf = NULL;
pasky 0:9858347c382d 97 m_replylen = 0;
pasky 0:9858347c382d 98
pasky 0:9858347c382d 99 PubNubRes res;
pasky 0:9858347c382d 100 res = http_send_headers(host);
pasky 0:9858347c382d 101 if (res != PNR_OK) {
pasky 0:9858347c382d 102 close();
pasky 0:9858347c382d 103 return res;
pasky 0:9858347c382d 104 }
pasky 0:9858347c382d 105
pasky 0:9858347c382d 106 res = http_recv_headers();
pasky 0:9858347c382d 107 if (res != PNR_OK) {
pasky 0:9858347c382d 108 if (m_replybuf) free(m_replybuf);
pasky 0:9858347c382d 109 close();
pasky 0:9858347c382d 110 return res;
pasky 0:9858347c382d 111 }
pasky 0:9858347c382d 112
pasky 0:9858347c382d 113 res = http_recv_content();
pasky 0:9858347c382d 114 if (res != PNR_OK) {
pasky 0:9858347c382d 115 if (m_replybuf) free(m_replybuf);
pasky 0:9858347c382d 116 close();
pasky 0:9858347c382d 117 return res;
pasky 0:9858347c382d 118 }
pasky 0:9858347c382d 119
pasky 0:9858347c382d 120 close();
pasky 0:9858347c382d 121 *reply = (char *) realloc(m_replybuf, m_replylen);
pasky 0:9858347c382d 122 *replylen = m_replylen;
pasky 0:9858347c382d 123 return PNR_OK;
pasky 0:9858347c382d 124 }
pasky 0:9858347c382d 125
pasky 0:9858347c382d 126 PubNubRes
pasky 0:9858347c382d 127 PubNubHTTP::http_send_headers(const char *host)
pasky 0:9858347c382d 128 {
pasky 0:9858347c382d 129 /* Finish the first line of the request. */
pasky 0:9858347c382d 130 sendstr(" HTTP/1.1\r\n");
pasky 0:9858347c382d 131 /* Finish HTTP request. */
pasky 0:9858347c382d 132 sendstr("Host: ");
pasky 0:9858347c382d 133 sendstr(host);
pasky 0:9858347c382d 134 sendstr("\r\nUser-Agent: PubNub-mbed/0.1\r\nConnection: close\r\n\r\n");
pasky 0:9858347c382d 135 if (!is_connected())
pasky 0:9858347c382d 136 return PNR_IO_ERROR;
pasky 0:9858347c382d 137 return PNR_OK;
pasky 0:9858347c382d 138 }
pasky 0:9858347c382d 139
pasky 0:9858347c382d 140 PubNubRes
pasky 0:9858347c382d 141 PubNubHTTP::http_recv_headers()
pasky 0:9858347c382d 142 {
pasky 0:9858347c382d 143 /* Now, read HTTP reply. */
pasky 0:9858347c382d 144 m_replybuf = (char *) malloc(PUBNUB_REPLY_MAXLEN+1);
pasky 0:9858347c382d 145
pasky 0:9858347c382d 146 /* First, receive headers. */
pasky 0:9858347c382d 147 /* XXX: For now, we assume complete headers will fit in m_replybuf. */
pasky 0:9858347c382d 148 m_replylen = receive_all(m_replybuf, PUBNUB_REPLY_MAXLEN);
pasky 0:9858347c382d 149 if (m_replylen < 0)
pasky 0:9858347c382d 150 return PNR_IO_ERROR;
pasky 0:9858347c382d 151 m_replybuf[m_replylen] = 0;
pasky 0:9858347c382d 152
pasky 0:9858347c382d 153 char *bufptr = m_replybuf;
pasky 0:9858347c382d 154
pasky 0:9858347c382d 155 /* Parse the first line. */
pasky 0:9858347c382d 156 if (strncmp(bufptr, "HTTP/1.", 7) || !bufptr[7] || !bufptr[8])
pasky 0:9858347c382d 157 return PNR_IO_ERROR;
pasky 0:9858347c382d 158 int http_code = atoi(bufptr+9);
pasky 0:9858347c382d 159 if (http_code / 100 != 2)
pasky 0:9858347c382d 160 return PNR_HTTP_ERROR;
pasky 0:9858347c382d 161 bufptr = strstr(bufptr, "\r\n");
pasky 0:9858347c382d 162 if (!bufptr)
pasky 0:9858347c382d 163 return PNR_IO_ERROR;
pasky 0:9858347c382d 164 bufptr += 2;
pasky 0:9858347c382d 165
pasky 0:9858347c382d 166 /* Parse the rest of the lines. */
pasky 0:9858347c382d 167 m_content_length = 0;
pasky 0:9858347c382d 168 bool is_chunked = false;
pasky 0:9858347c382d 169 char *newline;
pasky 0:9858347c382d 170 for (;; bufptr = newline+2) {
pasky 0:9858347c382d 171 newline = strstr(bufptr, "\r\n");
pasky 0:9858347c382d 172 if (!newline)
pasky 0:9858347c382d 173 return PNR_IO_ERROR;
pasky 0:9858347c382d 174 *newline = 0;
pasky 0:9858347c382d 175
pasky 0:9858347c382d 176 char h_chunked[] = "Transfer-Encoding: chunked";
pasky 0:9858347c382d 177 char h_length[] = "Content-Length: ";
pasky 0:9858347c382d 178 if (*bufptr == 0) {
pasky 0:9858347c382d 179 /* Empty line. End of headers. */
pasky 0:9858347c382d 180 bufptr += 2;
pasky 0:9858347c382d 181 break;
pasky 0:9858347c382d 182 } else if (!strncmp(bufptr, h_chunked, sizeof(h_chunked)-1)) {
pasky 0:9858347c382d 183 /* Transfer-Encoding: chunked */
pasky 0:9858347c382d 184 is_chunked = true;
pasky 0:9858347c382d 185 } else if (!strncmp(bufptr, h_length, sizeof(h_length)-1)) {
pasky 0:9858347c382d 186 /* Content-Length: ... */
pasky 0:9858347c382d 187 m_content_length = atoi(bufptr + sizeof(h_length)-1);
pasky 0:9858347c382d 188 }
pasky 0:9858347c382d 189 }
pasky 0:9858347c382d 190
pasky 0:9858347c382d 191 /* Possibly process a chunk header. */
pasky 0:9858347c382d 192 if (is_chunked) {
pasky 0:9858347c382d 193 m_content_length = atoi(bufptr);
pasky 0:9858347c382d 194 bufptr = strstr(bufptr, "\r\n");
pasky 0:9858347c382d 195 if (!bufptr)
pasky 0:9858347c382d 196 return PNR_IO_ERROR;
pasky 0:9858347c382d 197 bufptr += 2;
pasky 0:9858347c382d 198 }
pasky 0:9858347c382d 199
pasky 0:9858347c382d 200 /* Consolidate the buffer. */
pasky 0:9858347c382d 201 m_replylen -= bufptr - m_replybuf;
pasky 0:9858347c382d 202 memmove(m_replybuf, bufptr, m_replylen);
pasky 0:9858347c382d 203
pasky 0:9858347c382d 204 return PNR_OK;
pasky 0:9858347c382d 205 }
pasky 0:9858347c382d 206
pasky 0:9858347c382d 207 PubNubRes
pasky 0:9858347c382d 208 PubNubHTTP::http_recv_content()
pasky 0:9858347c382d 209 {
pasky 0:9858347c382d 210 /* Now, we are ready to process data! */
pasky 0:9858347c382d 211
pasky 0:9858347c382d 212 if (m_content_length > PUBNUB_REPLY_MAXLEN) {
pasky 0:9858347c382d 213 /* XXX: Actually, too much data, sorry. */
pasky 0:9858347c382d 214 DBG printf("too much data\r\n");
pasky 0:9858347c382d 215 return PNR_FORMAT_ERROR;
pasky 0:9858347c382d 216 }
pasky 0:9858347c382d 217
pasky 0:9858347c382d 218 if (m_replylen < m_content_length) {
pasky 0:9858347c382d 219 /* More data coming. */
pasky 0:9858347c382d 220 int recv2len = receive_all(m_replybuf + m_replylen, PUBNUB_REPLY_MAXLEN - m_replylen);
pasky 0:9858347c382d 221 if (recv2len < 0)
pasky 0:9858347c382d 222 return PNR_IO_ERROR;
pasky 0:9858347c382d 223 m_replylen += recv2len;
pasky 0:9858347c382d 224 }
pasky 0:9858347c382d 225
pasky 0:9858347c382d 226 if (m_replylen < m_content_length)
pasky 0:9858347c382d 227 return PNR_IO_ERROR; /* Incomplete data. */
pasky 0:9858347c382d 228 return PNR_OK;
pasky 0:9858347c382d 229 }
pasky 0:9858347c382d 230
pasky 0:9858347c382d 231
pasky 0:9858347c382d 232 /** Some utility routines. */
pasky 0:9858347c382d 233
pasky 0:9858347c382d 234 /* Split a JSON array (with arbitrary contents) to multiple NUL-terminated
pasky 0:9858347c382d 235 * C strings. */
pasky 0:9858347c382d 236 static PubNubRes
pasky 0:9858347c382d 237 split_array(char *buf, int len)
pasky 0:9858347c382d 238 {
pasky 0:9858347c382d 239 bool escaped = false, in_string = false;
pasky 0:9858347c382d 240 int bracket_level = 0;
pasky 0:9858347c382d 241 for (int i = 0; i < len; i++) {
pasky 0:9858347c382d 242 if (escaped) {
pasky 0:9858347c382d 243 escaped = false;
pasky 0:9858347c382d 244 } else if (in_string) {
pasky 0:9858347c382d 245 switch (buf[i]) {
pasky 0:9858347c382d 246 case '\\': escaped = true; break;
pasky 0:9858347c382d 247 case '"': in_string = false; break;
pasky 0:9858347c382d 248 default: break;
pasky 0:9858347c382d 249 }
pasky 0:9858347c382d 250 } else {
pasky 0:9858347c382d 251 switch (buf[i]) {
pasky 0:9858347c382d 252 case '"': in_string = true; break;
pasky 0:9858347c382d 253 case '[': case '{': bracket_level++; break;
pasky 0:9858347c382d 254 case ']': case '}': bracket_level--; break;
pasky 0:9858347c382d 255 /* if at root, split! */
pasky 0:9858347c382d 256 case ',': if (bracket_level == 0) buf[i] = 0; break;
pasky 0:9858347c382d 257 default: break;
pasky 0:9858347c382d 258 }
pasky 0:9858347c382d 259 }
pasky 0:9858347c382d 260 }
pasky 0:9858347c382d 261 DBG printf("parse %d %d %d\r\n", escaped, in_string, bracket_level);
pasky 0:9858347c382d 262 if (escaped || in_string || bracket_level > 0)
pasky 0:9858347c382d 263 return PNR_FORMAT_ERROR;
pasky 0:9858347c382d 264 return PNR_OK;
pasky 0:9858347c382d 265 }
pasky 0:9858347c382d 266
pasky 0:9858347c382d 267
pasky 0:9858347c382d 268 /** PubNub API. */
pasky 0:9858347c382d 269
pasky 0:9858347c382d 270 PubNub::PubNub(const char *publish_key_, const char *subscribe_key_, const char *origin_) :
pasky 0:9858347c382d 271 m_publish_key(publish_key_), m_subscribe_key(subscribe_key_), m_origin(origin_),
pasky 0:9858347c382d 272 m_replybuf(NULL), m_replylen(0)
pasky 0:9858347c382d 273 {
pasky 0:9858347c382d 274 strcpy(m_timetoken, "0");
pasky 0:9858347c382d 275 }
pasky 0:9858347c382d 276
pasky 0:9858347c382d 277 PubNub::~PubNub()
pasky 0:9858347c382d 278 {
pasky 0:9858347c382d 279 if (m_replybuf)
pasky 0:9858347c382d 280 free(m_replybuf);
pasky 0:9858347c382d 281 }
pasky 0:9858347c382d 282
pasky 0:9858347c382d 283 const char *
pasky 0:9858347c382d 284 PubNub::origin_hostname()
pasky 0:9858347c382d 285 {
pasky 0:9858347c382d 286 /* TODO: More generic URL handling */
pasky 0:9858347c382d 287 return m_origin + strlen("http://");
pasky 0:9858347c382d 288 }
pasky 0:9858347c382d 289
pasky 0:9858347c382d 290
pasky 0:9858347c382d 291 PubNubRes
pasky 0:9858347c382d 292 PubNub::publish(const char *channel, const char *message, char **reply)
pasky 0:9858347c382d 293 {
pasky 0:9858347c382d 294 PubNubHTTP http;
pasky 0:9858347c382d 295 PubNubRes res = http.http_connect(origin_hostname());
pasky 0:9858347c382d 296 if (res != PNR_OK)
pasky 0:9858347c382d 297 return res;
pasky 0:9858347c382d 298
pasky 0:9858347c382d 299 http.sendstr("GET /publish/");
pasky 0:9858347c382d 300 http.sendstr(m_publish_key);
pasky 0:9858347c382d 301 http.sendstr("/");
pasky 0:9858347c382d 302 http.sendstr(m_subscribe_key);
pasky 0:9858347c382d 303 http.sendstr("/0/");
pasky 0:9858347c382d 304 http.sendstr(channel);
pasky 0:9858347c382d 305 http.sendstr("/0/");
pasky 0:9858347c382d 306 http.sendstr_urlenc(message);
pasky 0:9858347c382d 307
pasky 0:9858347c382d 308 char *locreply;
pasky 0:9858347c382d 309 if (!reply)
pasky 0:9858347c382d 310 reply = &locreply;
pasky 0:9858347c382d 311 int replylen;
pasky 0:9858347c382d 312
pasky 0:9858347c382d 313 res = http.http_request_bh(origin_hostname(), reply, &replylen);
pasky 0:9858347c382d 314 if (res != PNR_OK)
pasky 0:9858347c382d 315 return res;
pasky 0:9858347c382d 316 bool success = (*reply)[1] == '1' && (*reply)[2] == ',';
pasky 0:9858347c382d 317 if (reply == &locreply)
pasky 0:9858347c382d 318 free(locreply);
pasky 0:9858347c382d 319 return success ? PNR_OK : PNR_PUBNUB_ERROR;
pasky 0:9858347c382d 320 }
pasky 0:9858347c382d 321
pasky 0:9858347c382d 322
pasky 0:9858347c382d 323 PubNubRes
pasky 0:9858347c382d 324 PubNub::subscribe(const char *channel, char **reply)
pasky 0:9858347c382d 325 {
pasky 0:9858347c382d 326 if (m_replybuf) {
pasky 0:9858347c382d 327 int prevlen = strlen(m_replybuf);
pasky 0:9858347c382d 328 //DBG printf("reply (%s) %d > %d: %d\r\n", m_replybuf, prevlen, m_replylen);
pasky 0:9858347c382d 329 if (prevlen < m_replylen) {
pasky 0:9858347c382d 330 /* Next message from stash-away buffer. */
pasky 0:9858347c382d 331 /* XXX: We can be either memory-frugal or CPU-frugal
pasky 0:9858347c382d 332 * here. We choose to be memory-frugal by copying
pasky 0:9858347c382d 333 * over messages many times, but we may want to make
pasky 0:9858347c382d 334 * this configurable. */
pasky 0:9858347c382d 335 m_replylen -= prevlen + 1;
pasky 0:9858347c382d 336 memmove(m_replybuf, m_replybuf + prevlen + 1, m_replylen);
pasky 0:9858347c382d 337 m_replybuf = (char *) realloc(m_replybuf, m_replylen);
pasky 0:9858347c382d 338 *reply = m_replybuf;
pasky 0:9858347c382d 339 return PNR_OK;
pasky 0:9858347c382d 340
pasky 0:9858347c382d 341 } else {
pasky 0:9858347c382d 342 /* That's all. free() and fetch new messages. */
pasky 0:9858347c382d 343 free(m_replybuf);
pasky 0:9858347c382d 344 m_replybuf = NULL;
pasky 0:9858347c382d 345 m_replylen = 0;
pasky 0:9858347c382d 346 }
pasky 0:9858347c382d 347 }
pasky 0:9858347c382d 348
pasky 0:9858347c382d 349 PubNubHTTP http;
pasky 0:9858347c382d 350 PubNubRes res;
pasky 0:9858347c382d 351 res = http.http_connect(origin_hostname());
pasky 0:9858347c382d 352 if (res != PNR_OK)
pasky 0:9858347c382d 353 return res;
pasky 0:9858347c382d 354
pasky 0:9858347c382d 355 http.sendstr("GET /subscribe/");
pasky 0:9858347c382d 356 http.sendstr(m_subscribe_key);
pasky 0:9858347c382d 357 http.sendstr("/");
pasky 0:9858347c382d 358 http.sendstr(channel);
pasky 0:9858347c382d 359 http.sendstr("/0/");
pasky 0:9858347c382d 360 http.sendstr(m_timetoken);
pasky 0:9858347c382d 361
pasky 0:9858347c382d 362 char *replybuf = NULL;
pasky 0:9858347c382d 363 int replylen;
pasky 0:9858347c382d 364 res = http.http_request_bh(origin_hostname(), &replybuf, &replylen);
pasky 0:9858347c382d 365 if (res != PNR_OK)
pasky 0:9858347c382d 366 goto error;
pasky 0:9858347c382d 367
pasky 0:9858347c382d 368 /* Process the reply, sets timetoken and m_replybuf, m_replylen. */
pasky 0:9858347c382d 369 res = subscribe_processjson(replybuf, replylen);
pasky 0:9858347c382d 370 if (res != PNR_OK)
pasky 0:9858347c382d 371 goto error;
pasky 0:9858347c382d 372 replybuf = NULL; // freed by processjosn
pasky 0:9858347c382d 373
pasky 0:9858347c382d 374 /* Split JSON array to messages. */
pasky 0:9858347c382d 375 res = split_array(m_replybuf, m_replylen);
pasky 0:9858347c382d 376 if (res != PNR_OK) {
pasky 0:9858347c382d 377 free(m_replybuf);
pasky 0:9858347c382d 378 goto error;
pasky 0:9858347c382d 379 }
pasky 0:9858347c382d 380
pasky 0:9858347c382d 381 *reply = m_replybuf;
pasky 0:9858347c382d 382 return res;
pasky 0:9858347c382d 383
pasky 0:9858347c382d 384 error:
pasky 0:9858347c382d 385 if (res == PNR_FORMAT_ERROR) {
pasky 0:9858347c382d 386 /* In case of PubNub protocol error, abort an ongoing
pasky 0:9858347c382d 387 * subscribe and start over. This means some messages
pasky 0:9858347c382d 388 * were lost, but allows us to recover from bad
pasky 0:9858347c382d 389 * situations, e.g. too many messages queued or
pasky 0:9858347c382d 390 * unexpected problem caused by a particular message. */
pasky 0:9858347c382d 391 strcpy(m_timetoken, "0");
pasky 0:9858347c382d 392 }
pasky 0:9858347c382d 393 if (reply)
pasky 0:9858347c382d 394 free(reply);
pasky 0:9858347c382d 395 m_replybuf = NULL;
pasky 0:9858347c382d 396 m_replylen = 0;
pasky 0:9858347c382d 397 return res;
pasky 0:9858347c382d 398 }
pasky 0:9858347c382d 399
pasky 0:9858347c382d 400 PubNubRes
pasky 0:9858347c382d 401 PubNub::subscribe_processjson(char *reply, int replylen)
pasky 0:9858347c382d 402 {
pasky 0:9858347c382d 403 if (reply[0] != '[' || reply[replylen-1] != ']'
pasky 0:9858347c382d 404 || reply[replylen-2] != '"') {
pasky 0:9858347c382d 405 DBG printf("bad reply '%s'\r\n", reply);
pasky 0:9858347c382d 406 return PNR_FORMAT_ERROR;
pasky 0:9858347c382d 407 }
pasky 0:9858347c382d 408
pasky 0:9858347c382d 409 /* Extract timetoken. */
pasky 0:9858347c382d 410 reply[replylen-2] = 0;
pasky 0:9858347c382d 411 int i;
pasky 0:9858347c382d 412 for (i = replylen-3; i > 0 && i > int(replylen-3 - (sizeof(m_timetoken)-1)); i--)
pasky 0:9858347c382d 413 if (reply[i] == '"')
pasky 0:9858347c382d 414 break;
pasky 0:9858347c382d 415 if (!i || reply[i-1] != ',' || replylen-2 - (i+1) >= 64) {
pasky 0:9858347c382d 416 DBG printf("bad reply '%s'\r\n", reply);
pasky 0:9858347c382d 417 return PNR_FORMAT_ERROR;
pasky 0:9858347c382d 418 }
pasky 0:9858347c382d 419 strcpy(m_timetoken, &reply[i+1]);
pasky 0:9858347c382d 420 reply[i-1] = 0; // terminate the [] message array
pasky 0:9858347c382d 421
pasky 0:9858347c382d 422 /* Empty reply? */
pasky 0:9858347c382d 423 if (i == 4) { /* "[[]" */
pasky 0:9858347c382d 424 free(reply);
pasky 0:9858347c382d 425 m_replybuf = NULL;
pasky 0:9858347c382d 426 m_replylen = 0;
pasky 0:9858347c382d 427 return PNR_OK;
pasky 0:9858347c382d 428 }
pasky 0:9858347c382d 429
pasky 0:9858347c382d 430 /* Extract the messages array. */
pasky 0:9858347c382d 431 if (reply[1] != '[' || reply[i-2] != ']') {
pasky 0:9858347c382d 432 DBG printf("bad reply end '%s'\r\n", reply);
pasky 0:9858347c382d 433 return PNR_FORMAT_ERROR;
pasky 0:9858347c382d 434 }
pasky 0:9858347c382d 435 reply[i-2] = 0;
pasky 0:9858347c382d 436
pasky 0:9858347c382d 437 /* Shrink memory buffer to bare minimum. */
pasky 0:9858347c382d 438 memmove(reply, reply + 2, i-2-2);
pasky 0:9858347c382d 439 m_replylen = i-2-2;
pasky 0:9858347c382d 440 m_replybuf = (char *) realloc(reply, m_replylen);
pasky 0:9858347c382d 441
pasky 0:9858347c382d 442 return PNR_OK;
pasky 0:9858347c382d 443 }
pasky 0:9858347c382d 444
pasky 0:9858347c382d 445
pasky 0:9858347c382d 446 PubNubRes
pasky 0:9858347c382d 447 PubNub::history(const char *channel, char **reply, int *replysize, int limit)
pasky 0:9858347c382d 448 {
pasky 0:9858347c382d 449 PubNubHTTP http;
pasky 0:9858347c382d 450 PubNubRes res;
pasky 0:9858347c382d 451 res = http.http_connect(origin_hostname());
pasky 0:9858347c382d 452 if (res != PNR_OK)
pasky 0:9858347c382d 453 return res;
pasky 0:9858347c382d 454
pasky 0:9858347c382d 455 http.sendstr("GET /history/");
pasky 0:9858347c382d 456 http.sendstr(m_subscribe_key);
pasky 0:9858347c382d 457 http.sendstr("/");
pasky 0:9858347c382d 458 http.sendstr(channel);
pasky 0:9858347c382d 459 http.sendstr("/0/");
pasky 0:9858347c382d 460 char limitbuf[8]; snprintf(limitbuf, sizeof(limitbuf), "%d", limit);
pasky 0:9858347c382d 461 http.sendstr(limitbuf);
pasky 0:9858347c382d 462
pasky 0:9858347c382d 463 char *replybuf = NULL;
pasky 0:9858347c382d 464 int replylen;
pasky 0:9858347c382d 465 res = http.http_request_bh(origin_hostname(), &replybuf, &replylen);
pasky 0:9858347c382d 466 if (res != PNR_OK)
pasky 0:9858347c382d 467 goto error;
pasky 0:9858347c382d 468
pasky 0:9858347c382d 469 /* Extract from the array and split it. */
pasky 0:9858347c382d 470
pasky 0:9858347c382d 471 if (replybuf[0] != '[' || replybuf[replylen-1] != ']') {
pasky 0:9858347c382d 472 res = PNR_FORMAT_ERROR;
pasky 0:9858347c382d 473 goto error;
pasky 0:9858347c382d 474 }
pasky 0:9858347c382d 475
pasky 0:9858347c382d 476 replylen -= 2;
pasky 0:9858347c382d 477 if (replylen == 0) { /* The reply was [] */
pasky 0:9858347c382d 478 free(replybuf);
pasky 0:9858347c382d 479 *reply = NULL;
pasky 0:9858347c382d 480 *replysize = 0;
pasky 0:9858347c382d 481 return PNR_OK;
pasky 0:9858347c382d 482 }
pasky 0:9858347c382d 483
pasky 0:9858347c382d 484 memmove(replybuf, replybuf + 1, replylen);
pasky 0:9858347c382d 485 replybuf[replylen] = 0;
pasky 0:9858347c382d 486
pasky 0:9858347c382d 487 res = split_array(replybuf, replylen);
pasky 0:9858347c382d 488 if (res != PNR_OK)
pasky 0:9858347c382d 489 goto error;
pasky 0:9858347c382d 490
pasky 0:9858347c382d 491 *reply = replybuf;
pasky 0:9858347c382d 492 *replysize = replylen;
pasky 0:9858347c382d 493 return PNR_OK;
pasky 0:9858347c382d 494
pasky 0:9858347c382d 495 error:
pasky 0:9858347c382d 496 free(replybuf);
pasky 0:9858347c382d 497 return res;
pasky 0:9858347c382d 498 }
pasky 0:9858347c382d 499
pasky 0:9858347c382d 500
pasky 0:9858347c382d 501 PubNubRes
pasky 0:9858347c382d 502 PubNub::time(char *ts)
pasky 0:9858347c382d 503 {
pasky 0:9858347c382d 504 PubNubHTTP http;
pasky 0:9858347c382d 505 PubNubRes res = http.http_connect(origin_hostname());
pasky 0:9858347c382d 506 if (res != PNR_OK)
pasky 0:9858347c382d 507 return res;
pasky 0:9858347c382d 508
pasky 0:9858347c382d 509 http.sendstr("GET /time/0");
pasky 0:9858347c382d 510
pasky 0:9858347c382d 511 char *reply;
pasky 0:9858347c382d 512 int replylen;
pasky 0:9858347c382d 513 res = http.http_request_bh(origin_hostname(), &reply, &replylen);
pasky 0:9858347c382d 514 if (res != PNR_OK)
pasky 0:9858347c382d 515 return res;
pasky 0:9858347c382d 516
pasky 0:9858347c382d 517 if (replylen < 3 || replylen > 32 || reply[0] != '[' || reply[replylen-1] != ']') {
pasky 0:9858347c382d 518 free(reply);
pasky 0:9858347c382d 519 return PNR_FORMAT_ERROR;
pasky 0:9858347c382d 520 }
pasky 0:9858347c382d 521
pasky 0:9858347c382d 522 replylen -= 2;
pasky 0:9858347c382d 523 memcpy(ts, reply + 1, replylen);
pasky 0:9858347c382d 524 ts[replylen] = 0;
pasky 0:9858347c382d 525
pasky 0:9858347c382d 526 free(reply);
pasky 0:9858347c382d 527 return PNR_OK;
pasky 0:9858347c382d 528 }