Build Realtime Apps With The Real-Time Network - the mbed PubNub API+SDK
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.
|
PubNub.cpp@0:9858347c382d, 2014-03-02 (annotated)
- 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?
User | Revision | Line number | New 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 | } |