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

Dependents:   PubNubDemo Cellular_PubNubDemo lpc4088_ebb_ublox_Cellular_PubNubDemo PubNubDemo_LPC4088 ... more

Fork of PubNub by Petr Baudis

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:
Thu Nov 20 21:30:10 2014 +0000
Revision:
8:c9f79982b5ca
Parent:
7:a7bbafe53f2d
Parent:
6:09d0a383fdde
Merge

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"
mazgch 6:09d0a383fdde 4 #include "TCPSocketConnection.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;
mkilivan 7:a7bbafe53f2d 440 reply[m_replylen] = 0;
pasky 0:9858347c382d 441 m_replybuf = (char *) realloc(reply, m_replylen);
pasky 0:9858347c382d 442
pasky 0:9858347c382d 443 return PNR_OK;
pasky 0:9858347c382d 444 }
pasky 0:9858347c382d 445
pasky 0:9858347c382d 446
pasky 0:9858347c382d 447 PubNubRes
pasky 0:9858347c382d 448 PubNub::history(const char *channel, char **reply, int *replysize, int limit)
pasky 0:9858347c382d 449 {
pasky 0:9858347c382d 450 PubNubHTTP http;
pasky 0:9858347c382d 451 PubNubRes res;
pasky 0:9858347c382d 452 res = http.http_connect(origin_hostname());
pasky 0:9858347c382d 453 if (res != PNR_OK)
pasky 0:9858347c382d 454 return res;
pasky 0:9858347c382d 455
pasky 0:9858347c382d 456 http.sendstr("GET /history/");
pasky 0:9858347c382d 457 http.sendstr(m_subscribe_key);
pasky 0:9858347c382d 458 http.sendstr("/");
pasky 0:9858347c382d 459 http.sendstr(channel);
pasky 0:9858347c382d 460 http.sendstr("/0/");
pasky 0:9858347c382d 461 char limitbuf[8]; snprintf(limitbuf, sizeof(limitbuf), "%d", limit);
pasky 0:9858347c382d 462 http.sendstr(limitbuf);
pasky 0:9858347c382d 463
pasky 0:9858347c382d 464 char *replybuf = NULL;
pasky 0:9858347c382d 465 int replylen;
pasky 0:9858347c382d 466 res = http.http_request_bh(origin_hostname(), &replybuf, &replylen);
pasky 0:9858347c382d 467 if (res != PNR_OK)
pasky 0:9858347c382d 468 goto error;
pasky 0:9858347c382d 469
pasky 0:9858347c382d 470 /* Extract from the array and split it. */
pasky 0:9858347c382d 471
pasky 0:9858347c382d 472 if (replybuf[0] != '[' || replybuf[replylen-1] != ']') {
pasky 0:9858347c382d 473 res = PNR_FORMAT_ERROR;
pasky 0:9858347c382d 474 goto error;
pasky 0:9858347c382d 475 }
pasky 0:9858347c382d 476
pasky 0:9858347c382d 477 replylen -= 2;
pasky 0:9858347c382d 478 if (replylen == 0) { /* The reply was [] */
pasky 0:9858347c382d 479 free(replybuf);
pasky 0:9858347c382d 480 *reply = NULL;
pasky 0:9858347c382d 481 *replysize = 0;
pasky 0:9858347c382d 482 return PNR_OK;
pasky 0:9858347c382d 483 }
pasky 0:9858347c382d 484
pasky 0:9858347c382d 485 memmove(replybuf, replybuf + 1, replylen);
pasky 0:9858347c382d 486 replybuf[replylen] = 0;
pasky 0:9858347c382d 487
pasky 0:9858347c382d 488 res = split_array(replybuf, replylen);
pasky 0:9858347c382d 489 if (res != PNR_OK)
pasky 0:9858347c382d 490 goto error;
pasky 0:9858347c382d 491
pasky 0:9858347c382d 492 *reply = replybuf;
pasky 0:9858347c382d 493 *replysize = replylen;
pasky 0:9858347c382d 494 return PNR_OK;
pasky 0:9858347c382d 495
pasky 0:9858347c382d 496 error:
pasky 0:9858347c382d 497 free(replybuf);
pasky 0:9858347c382d 498 return res;
pasky 0:9858347c382d 499 }
pasky 0:9858347c382d 500
pasky 0:9858347c382d 501
pasky 0:9858347c382d 502 PubNubRes
pasky 0:9858347c382d 503 PubNub::time(char *ts)
pasky 0:9858347c382d 504 {
pasky 0:9858347c382d 505 PubNubHTTP http;
pasky 0:9858347c382d 506 PubNubRes res = http.http_connect(origin_hostname());
pasky 0:9858347c382d 507 if (res != PNR_OK)
pasky 0:9858347c382d 508 return res;
pasky 0:9858347c382d 509
pasky 0:9858347c382d 510 http.sendstr("GET /time/0");
pasky 0:9858347c382d 511
pasky 0:9858347c382d 512 char *reply;
pasky 0:9858347c382d 513 int replylen;
pasky 0:9858347c382d 514 res = http.http_request_bh(origin_hostname(), &reply, &replylen);
pasky 0:9858347c382d 515 if (res != PNR_OK)
pasky 0:9858347c382d 516 return res;
pasky 0:9858347c382d 517
pasky 0:9858347c382d 518 if (replylen < 3 || replylen > 32 || reply[0] != '[' || reply[replylen-1] != ']') {
pasky 0:9858347c382d 519 free(reply);
pasky 0:9858347c382d 520 return PNR_FORMAT_ERROR;
pasky 0:9858347c382d 521 }
pasky 0:9858347c382d 522
pasky 0:9858347c382d 523 replylen -= 2;
pasky 0:9858347c382d 524 memcpy(ts, reply + 1, replylen);
pasky 0:9858347c382d 525 ts[replylen] = 0;
pasky 0:9858347c382d 526
pasky 0:9858347c382d 527 free(reply);
pasky 0:9858347c382d 528 return PNR_OK;
pasky 0:9858347c382d 529 }